diff --git a/2012-07-07-nsindexset.md b/2012-07-07-nsindexset.md
index 9a34fb3e..ffa692e8 100644
--- a/2012-07-07-nsindexset.md
+++ b/2012-07-07-nsindexset.md
@@ -1,6 +1,6 @@
---
title: NSIndexSet
-author: Mattt Thompson
+author: Mattt
category: Cocoa
tags: nshipster
excerpt: "NSIndexSet (like its mutable counterpart, NSMutableIndexSet) is a sorted collection of unique unsigned integers. Think of it like an NSRange that supports non-contiguous series. It has wicked fast operations for finding indexes in ranges or set intersections, and comes with all of the convenience methods you'd expect in a Foundation collection class."
diff --git a/2012-07-14-nscache.md b/2012-07-14-nscache.md
index 7f816b3b..513fa7f8 100644
--- a/2012-07-14-nscache.md
+++ b/2012-07-14-nscache.md
@@ -1,6 +1,6 @@
---
title: NSCache
-author: Mattt Thompson
+author: Mattt
category: Cocoa
tags: nshipster
excerpt: "Poor NSCache, always being overshadowed by NSMutableDictionary. It's as if no one knew how it provides all of that garbage collection behavior that developers take great pains to re-implement themselves."
@@ -41,7 +41,7 @@ There's also a whole part about controlling whether objects are automatically ev
Despite all of this, developers should be using `NSCache` a lot more than they currently are. Anything in your project that you call a "cache", but isn't `NSCache` would be prime candidates for replacement. But if you do, just be sure to stick to the classics: `objectForKey:`, `setObject:forKey:` & `removeObjectForKey:`.
-Still not convinved? As a parting gift, we'll even make it easier, via a little subscripting majick:
+Still not convinced? As a parting gift, we'll even make it easier, via a little subscripting majick:
```swift
extension NSCache {
@@ -58,4 +58,7 @@ extension NSCache {
}
}
}
-```
\ No newline at end of file
+```
+
+> **Note:** Due to changes in Objective-C generics in Swift 3, the
+> subscript given above will only work in Swift 2.3 and earlier.
diff --git a/2012-07-24-nssortdescriptor.md b/2012-07-24-nssortdescriptor.md
index 77037028..16692052 100644
--- a/2012-07-24-nssortdescriptor.md
+++ b/2012-07-24-nssortdescriptor.md
@@ -1,19 +1,18 @@
---
title: NSSortDescriptor
-author: Mattt Thompson
+author: Mattt
category: Cocoa
tags: nshipster
excerpt: "Sorting: it's the mainstay of Computer Science 101 exams and whiteboarding interview questions. But when was the last time you actually needed to know how to implement Quicksort yourself?"
status:
- swift: 2.2
- reviewed: April 10, 2016
+ swift: 1.1
---
Sorting: it's the mainstay of Computer Science 101 exams and whiteboarding interview questions. But when was the last time you actually needed to know how to implement Quicksort yourself?
When making apps, sorting is just something you can assume to be fast, and utility is a function of convenience and clarity of intention. And when it comes to that, you'd be hard-pressed to find a better implementation than Foundation's `NSSortDescriptor`.
-* * *
+---
`NSSortDescriptor` objects are constructed with the following parameters:
@@ -28,7 +27,7 @@ Collection classes like `NSArray` and `NSSet` have methods to return sorted arra
To put that into more practical terms, consider a `Person` class with properties for `firstName` & `lastName` of type `NSString *`, and `age`, which is an `NSUInteger`.
-~~~{swift}
+```swift
class Person: NSObject {
let firstName: String
let lastName: String
@@ -44,9 +43,9 @@ class Person: NSObject {
return "\(firstName) \(lastName)"
}
}
-~~~
+```
-~~~{objective-c}
+```objc
@interface Person : NSObject
@property NSString *firstName;
@property NSString *lastName;
@@ -60,12 +59,12 @@ class Person: NSObject {
}
@end
-~~~
+```
Given the following dataset:
| `firstName` | `lastName` | `age` |
-|-------------|------------|-------|
+| ----------- | ---------- | ----- |
| Alice | Smith | 24 |
| Bob | Jones | 27 |
| Charlie | Smith | 33 |
@@ -73,15 +72,15 @@ Given the following dataset:
Here are some of the different ways they can be sorted by combinations of `NSSortDescriptor`:
-~~~{swift}
+```swift
let alice = Person(firstName: "Alice", lastName: "Smith", age: 24)
let bob = Person(firstName: "Bob", lastName: "Jones", age: 27)
let charlie = Person(firstName: "Charlie", lastName: "Smith", age: 33)
let quentin = Person(firstName: "Quentin", lastName: "Alberts", age: 31)
let people = [alice, bob, charlie, quentin]
-let firstNameSortDescriptor = NSSortDescriptor(key: "firstName", ascending: true, selector: #selector(NSString.localizedStandardCompare(_:)))
-let lastNameSortDescriptor = NSSortDescriptor(key: "lastName", ascending: true, selector: #selector(NSString.localizedStandardCompare(_:)))
+let firstNameSortDescriptor = NSSortDescriptor(key: "firstName", ascending: true, selector: "localizedStandardCompare:")
+let lastNameSortDescriptor = NSSortDescriptor(key: "lastName", ascending: true, selector: "localizedStandardCompare:")
let ageSortDescriptor = NSSortDescriptor(key: "age", ascending: false)
let sortedByAge = (people as NSArray).sortedArrayUsingDescriptors([ageSortDescriptor])
@@ -92,9 +91,9 @@ let sortedByFirstName = (people as NSArray).sortedArrayUsingDescriptors([firstNa
let sortedByLastNameFirstName = (people as NSArray).sortedArrayUsingDescriptors([lastNameSortDescriptor, firstNameSortDescriptor])
// "Quentin Alberts", "Bob Jones", "Alice Smith", "Charlie Smith"
-~~~
+```
-~~~{objective-c}
+```objc
NSArray *firstNames = @[ @"Alice", @"Bob", @"Charlie", @"Quentin" ];
NSArray *lastNames = @[ @"Smith", @"Jones", @"Smith", @"Alberts" ];
NSArray *ages = @[ @24, @27, @33, @31 ];
@@ -127,9 +126,9 @@ NSLog(@"By first name: %@", [people sortedArrayUsingDescriptors:@[firstNameSortD
NSLog(@"By last name, first name: %@", [people sortedArrayUsingDescriptors:@[lastNameSortDescriptor, firstNameSortDescriptor]]);
// "Quentin Alberts", "Bob Jones", "Alice Smith", "Charlie Smith"
-~~~
+```
-* * *
+---
`NSSortDescriptor` can be found throughout Foundation and other system frameworks, playing an especially prominent role in Core Data. Anytime your own classes need to define sort ordering, follow the convention of specifying a `sortDescriptors` parameter as appropriate.
diff --git a/2012-07-31-datecomponents.md b/2012-07-31-datecomponents.md
new file mode 100644
index 00000000..32d098d9
--- /dev/null
+++ b/2012-07-31-datecomponents.md
@@ -0,0 +1,428 @@
+---
+title: DateComponents
+author: Mattt
+category: Cocoa
+excerpt: >-
+ `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.
+revisions:
+ "2012-07-31": Original Publication
+ "2018-10-10": Expanded details
+status:
+ swift: 4.2
+ reviewed: October 10, 2018
+---
+
+There are as many mnemonic devices for making sense of time
+as the day is long.
+["Spring ahead, Fall back"](https://en.wikipedia.org/wiki/Daylight_saving_time).
+[That knuckle trick for remembering the lengths of months.](https://en.wikipedia.org/wiki/Knuckle_mnemonic)
+Musical theater aficionados can tell you in quick measure
+[the length of a year in minutes](https://en.wikipedia.org/wiki/Seasons_of_Love).
+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.](/timeinterval-date-dateinterval),
+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 10 days
+or the tenth day of the tenth month in the year 2018:
+
+```swift
+import Foundation
+
+let calendar = Calendar.current
+let dateComponents = DateComponents(calendar: calendar,
+ year: 2018,
+ month: 10,
+ day: 10)
+
+// DateComponents as a date specifier
+let date = calendar.date(from: dateComponents)! // 2018-10-10
+
+// DateComponents as a duration of time
+calendar.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:)`:
+
+```swift
+let date = Date() // 2018-10-10T10:00:00+00:00
+let calendar = Calendar.current
+calendar.dateComponents([.year, .month, .day], from: date)
+// {{ page.updated_on | date: '(year: %Y, month: %-M, day: %-d)' }}
+```
+
+Each property in `DateComponents`
+has a corresponding entry in the
+[`Calendar.Component` enumeration](https://developer.apple.com/documentation/foundation/calendar/component).
+
+{% info %}
+For best results,
+specify only the date components / calendar units that you're interested in.
+{% endinfo %}
+
+For reference,
+here's what the `dateComponents(_:from:)` method produces
+when you specify all of the available calendar units:
+
+```swift
+import Foundation
+
+let date = Date() // 2018-10-10T10:00:00+00:00
+let calendar = Calendar.current
+let dateComponents = calendar.dateComponents(
+ [.calendar, .timeZone,
+ .era, .quarter,
+ .year, .month, .day,
+ .hour, .minute, .second, .nanosecond,
+ .weekday, .weekdayOrdinal,
+ .weekOfMonth, .weekOfYear, .yearForWeekOfYear],
+ from: date)
+```
+
+| Component | Value |
+| ------------------- | ------------------- |
+| `calendar` | gregorian |
+| `timeZone` | America/Los_Angeles |
+| `era` | 1 |
+| `quarter` | 0 |
+| `year` | 2018 |
+| `month` | 10 |
+| `day` | 10 |
+| `hour` | 10 |
+| `minute` | 0 |
+| `second` | 0 |
+| `nanosecond` | 0 |
+| `weekday` | 4 |
+| `weekdayOrdinal` | 2 |
+| `weekOfMonth` | 2 |
+| `weekOfYear` | 41 |
+| `yearForWeekOfYear` | 2018 |
+| `isLeapMonth` | false |
+
+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](https://en.wikipedia.org/wiki/Calendar_era):
+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](https://en.wikipedia.org/wiki/Calendar_year#Quarters)
+(Q1, Q2, Q3, Q4).
+
+{% error %}
+
+In iOS 12 and macOS Mojave,
+the `dateComponents(_:from:)` method
+doesn't populate the `quarter` property
+for the returned value, even with the unit is specified.
+See [rdar://35247464](http://www.openradar.me/35247464).
+
+As a workaround,
+you can use `DateFormatter` to generate a string
+using the date format `"Q"`
+and parse its integer value:
+
+```swift
+let formatter = DateFormatter()
+formatter.dateFormat = "Q"
+Int(formatter.string(from: Date())) // 4
+```
+
+{% enderror %}
+
+### Weekday, Weekday Ordinal, and Week of Month
+
+Weekdays are given integer values starting with
+1 for Sunday
+and ending with 7 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](https://en.wikipedia.org/wiki/ISO_week_date).
+
+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`
+
+{% warning %}
+
+In contrast to the `year` date component,
+`yearForWeekOfYear` has a negative value
+for years before the common era.
+For example,
+a date in the year 47 BC
+has a `yearForWeekOfYear` equal to `-46`
+(the off-by-one value is a consequence of how the year 0 is handled).
+
+{% endwarning %}
+
+### 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.
+
+```swift
+var date: Date?
+
+// Bad
+let timestamp = "2018-10-03"
+let formatter = ISO8601DateFormatter()
+formatter.formatOptions =
+ [.withFullDate, .withDashSeparatorInDate]
+date = formatter.date(from: timestamp)
+
+// Good
+let calendar = Calendar.current
+let dateComponents =
+ 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`.
+
+{% info %}
+Ranges for some units can also vary between calendars;
+for example, a `month` value of `13` is valid in the Coptic calendar,
+but invalid in the Gregorian calendar.
+{% endinfo %}
+
+### 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:)`:
+
+```swift
+let date = Date() // 2018-10-10T10:00:00+00:00
+let calendar = Calendar.current
+
+var beginningOfMonth: Date?
+
+// OK
+let dateComponents =
+ calendar.dateComponents([.year, .month], from: date)
+beginningOfMonth = calendar.date(from: dateComponents)
+
+// Better
+beginningOfMonth =
+ 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?
+
+```swift
+let date = Date() // 2018-10-10T10:00:00+00:00
+let calendar = Calendar.current
+
+let monthInterval =
+ 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:)`:
+
+```swift
+let date = Date() // 2018-10-10T10:00:00+00:00
+let calendar = Calendar.current
+
+var tomorrow: Date?
+
+// Bad
+tomorrow = date.addingTimeInterval(60 * 60 * 24)
+
+// Good
+tomorrow = 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.
+
+```swift
+let date = Date()
+let calendar = Calendar.current
+
+// Adding a year
+calendar.date(byAdding: .year, value: 1, to: date)
+
+// Adding a year and a day
+let dateComponents = 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:
+
+```swift
+let dateComponents =
+ calendar.dateComponents([.hour,
+ .minute,
+ .second,
+ .nanosecond],
+ from: date)
+
+tomorrow = calendar.nextDate(after: date,
+ matching: dateComponents,
+ matchingPolicy: .nextTime,
+ repeatedTimePolicy: .first,
+ direction: .forward)
+```
+
+{% info %}
+
+If this seems like a lot of work,
+remember that time is complicated and requires precision.
+There's a great explanation of the matching and repeated time policies
+[buried in the official documentation](https://developer.apple.com/documentation/foundation/nscalendar/1413938-enumeratedates),
+so be sure to check that out.
+
+{% endinfo %}
+
+---
+
+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.
diff --git a/2012-07-31-nsdatecomponents.md b/2012-07-31-nsdatecomponents.md
deleted file mode 100644
index 7ad1567e..00000000
--- a/2012-07-31-nsdatecomponents.md
+++ /dev/null
@@ -1,135 +0,0 @@
----
-title: NSDateComponents
-author: Mattt Thompson
-category: Cocoa
-excerpt: "NSDateComponents serves an important role in Foundation's date and time APIs. By itself, it's nothing impressive—just a container for information about a date (its month, year, day of month, week of year, or whether that month is a leap month). However, combined with NSCalendar, NSDateComponents becomes a remarkably convenient interchange format for calendar calculations."
-status:
- swift: 2.0
- reviewed: September 19, 2015
----
-
-`NSDateComponents` serves an important role in Foundation's date and time APIs. By itself, it's nothing impressive—just a container for information about a date (its month, year, day of month, week of year, or whether that month is a leap month). However, combined with `NSCalendar`, `NSDateComponents` becomes a remarkably convenient interchange format for calendar calculations.
-
-Whereas dates represent a particular moment in time, date components depend on which calendar system is being used to represent them. Very often, this will differ wildly from what many of us may be used to with the [Gregorian Calendar](http://en.wikipedia.org/wiki/Gregorian_calendar). For example, the [Islamic Calendar](http://en.wikipedia.org/wiki/Islamic_calendar) has 354 or 355 days in a year, whereas the [Buddhist calendar](http://en.wikipedia.org/wiki/Buddhist_calendar) may have 354, 355, 384, or 385 days, depending on the year.
-
-## Extracting Components From Dates
-
-`NSDateComponents` can be initialized and manipulated manually, but most often, they're extracted from a specified date, using `NSCalendar -components:fromDate:`:
-
-~~~{swift}
-let calendar = NSCalendar.currentCalendar()
-let date = NSDate()
-let components = calendar.components([.Month, .Day], fromDate: date)
-~~~
-
-~~~{objective-c}
-NSCalendar *calendar = [NSCalendar currentCalendar];
-NSDate *date = [NSDate date];
-[calendar components:(NSCalendarUnitDay | NSCalendarUnitMonth) fromDate:date];
-~~~
-
-The `components` parameter is a [bitmask](http://en.wikipedia.org/wiki/Bitmask) of the date component values to retrieve, with many to choose from:
-
-~~~{swift}
-NSCalendarUnit.Era
-NSCalendarUnit.Year
-NSCalendarUnit.Month
-NSCalendarUnit.Day
-NSCalendarUnit.Hour
-NSCalendarUnit.Minute
-NSCalendarUnit.Second
-NSCalendarUnit.Weekday
-NSCalendarUnit.WeekdayOrdinal
-NSCalendarUnit.Quarter
-NSCalendarUnit.WeekOfMonth
-NSCalendarUnit.WeekOfYear
-NSCalendarUnit.YearForWeekOfYear
-NSCalendarUnit.Calendar
-NSCalendarUnit.TimeZone
-~~~
-
-~~~{objective-c}
-NSCalendarUnitEra
-NSCalendarUnitYear
-NSCalendarUnitMonth
-NSCalendarUnitDay
-NSCalendarUnitHour
-NSCalendarUnitMinute
-NSCalendarUnitSecond
-NSCalendarUnitWeekday
-NSCalendarUnitWeekdayOrdinal
-NSCalendarUnitQuarter
-NSCalendarUnitWeekOfMonth
-NSCalendarUnitWeekOfYear
-NSCalendarUnitYearForWeekOfYear
-NSCalendarUnitCalendar
-NSCalendarUnitTimeZone
-~~~
-
-> Since it would be expensive to compute all of the possible values, specify only the components that will be used in subsequent calculations (joining with `|`, the bitwise `OR` operator).
-
-## Relative Date Calculations
-
-`NSDateComponents` objects can be used to do relative date calculations. To determining the date yesterday, next week, or 5 hours and 30 minutes from now, use `NSCalendar -dateByAddingComponents:toDate:options:`:
-
-~~~{swift}
-let calendar = NSCalendar.currentCalendar()
-let date = NSDate()
-
-let components = NSDateComponents()
-components.weekOfYear = 1
-components.hour = 12
-
-print("1 week and 12 hours from now: \(calendar.dateByAddingComponents(components, toDate: date, options: []))")
-~~~
-
-~~~{objective-c}
-NSCalendar *calendar = [NSCalendar currentCalendar];
-NSDate *date = [NSDate date];
-
-NSDateComponents *components = [[NSDateComponents alloc] init];
-[components setWeekOfYear:1];
-[components setHour:12];
-
-NSLog(@"1 week and twelve hours from now: %@", [calendar dateByAddingComponents:components toDate:date options:0]);
-~~~
-
-## Creating Dates from Components
-
-Perhaps the most powerful feature of `NSDateComponents`, however, is the ability to go the opposite direction—creating an `NSDate` object from components. `NSCalendar -dateFromComponents:` is the method used for this purpose:
-
-~~~{swift}
-let calendar = NSCalendar(identifier: NSCalendarIdentifierGregorian)
-
-let components = NSDateComponents()
-components.year = 1987
-components.month = 3
-components.day = 17
-components.hour = 14
-components.minute = 20
-components.second = 0
-
-let date = calendar?.dateFromComponents(components)
-~~~
-
-~~~{objective-c}
-NSCalendar *calendar = [NSCalendar currentCalendar];
-
-NSDateComponents *components = [[NSDateComponents alloc] init];
-[components setYear:1987];
-[components setMonth:3];
-[components setDay:17];
-[components setHour:14];
-[components setMinute:20];
-[components setSecond:0];
-
-NSDate *date = [calendar dateFromComponents:components];
-~~~
-
-What's particularly interesting about this approach is that a date can be determined by information other than the normal month/day/year approach. So long as a date can be uniquely determined from the provided information, you'll get a result. For example, specifying the year 2013, and the 316th day of the year would return an `NSDate` for 11/12/2013 at midnight (because no time was specified, all time components default to 0).
-
-> Note that passing inconsistent components will either result in some information being discarded, or `nil` being returned.
-
-* * *
-
-`NSDateComponents` and its relationship to `NSCalendar` highlight the distinct advantage having a pedantically-engineered framework like Foundation at your disposal. You may not be doing calendar calculations every day, but when the time comes, knowing how to use `NSDateComponents` will save you eons of frustration.
diff --git a/2012-08-06-cfstringtransform.md b/2012-08-06-cfstringtransform.md
index cc89b8f5..08bd46ad 100644
--- a/2012-08-06-cfstringtransform.md
+++ b/2012-08-06-cfstringtransform.md
@@ -1,6 +1,6 @@
---
title: CFStringTransform
-author: Mattt Thompson
+author: Mattt
category: Cocoa
tags: nshipster, popular
excerpt: "NSString is the crown jewel of Foundation. But as powerful as it is, one would be remiss not to mention its toll-free bridged cousin, CFMutableString—or more specifically, CFStringTransform."
@@ -15,7 +15,7 @@ There are two indicators that tell you everything you need to know about how nic
`NSString` is the crown jewel of Foundation. In an age where other languages _still_ struggle to handle Unicode correctly, `NSString` is especially impressive. Not content to _just work_ with whatever is thrown at it, `NSString` can parse strings into linguistic tags, determine the dominant language of the content, and convert between every string encoding imaginable. It's unfairly good.
-But as powerful as `NSString` / `NSMutableString` are, one would be remiss not to mention their [toll-free bridged](http://developer.apple.com/library/ios/#documentation/CoreFoundation/Conceptual/CFDesignConcepts/Articles/tollFreeBridgedTypes.html) cousin, `CFMutableString`—or more specifically, `CFStringTransform`.
+But as powerful as `NSString` / `NSMutableString` are, one would be remiss not to mention their [toll-free bridged](https://developer.apple.com/library/ios/#documentation/CoreFoundation/Conceptual/CFDesignConcepts/Articles/tollFreeBridgedTypes.html) cousin, `CFMutableString`—or more specifically, `CFStringTransform`.
As denoted by the `CF` prefix, `CFStringTransform` is part of Core Foundation. The function takes the following arguments, and returns a `Boolean` for whether or not the transform was successful:
@@ -38,7 +38,7 @@ As denoted by the `CF` prefix, `CFStringTransform` is part of Core Foundation. T
With the notable exception of English (and its delightful spelling inconsistencies), writing systems generally encode speech sounds into a consistent written representation. European languages generally use the Latin alphabet (with a few added diacritics), Russian uses Cyrillic, Japanese uses Hiragana & Katakana, and Thai, Korean, & Arabic each have their own scripts.
-Although each language has a particular inventory of sounds, some of which other languages may lack, the overlap across all of the major writing systems is remarkably high—enough so that one can rather effectively [transliterate](http://en.wikipedia.org/wiki/Transliteration) (not to be confused with [translation](http://en.wikipedia.org/wiki/Translation)) from one script to another.
+Although each language has a particular inventory of sounds, some of which other languages may lack, the overlap across all of the major writing systems is remarkably high—enough so that one can rather effectively [transliterate](https://en.wikipedia.org/wiki/Transliteration) (not to be confused with [translation](https://en.wikipedia.org/wiki/Translation)) from one script to another.
`CFStringTransform` can transliterate back and forth between Latin and Arabic, Cyrillic, Greek, Korean (Hangul), Hebrew, Japanese (Hiragana & Katakana), Mandarin Chinese, and Thai.
@@ -52,52 +52,52 @@ Although each language has a particular inventory of sounds, some of which other
-
kCFStringTransformLatinArabic
+
kCFStringTransformLatinArabic
mrḥbạ
مرحبا
-
kCFStringTransformLatinCyrillic
+
kCFStringTransformLatinCyrillic
privet
привет
-
kCFStringTransformLatinGreek
+
kCFStringTransformLatinGreek
geiá sou
γειά σου
-
kCFStringTransformLatinHangul
+
kCFStringTransformLatinHangul
annyeonghaseyo
안녕하세요
-
kCFStringTransformLatinHebrew
+
kCFStringTransformLatinHebrew
şlwm
שלום
-
kCFStringTransformLatinHiragana
+
kCFStringTransformLatinHiragana
hiragana
ひらがな
-
kCFStringTransformLatinKatakana
+
kCFStringTransformLatinKatakana
katakana
カタカナ
-
kCFStringTransformLatinThai
+
kCFStringTransformLatinThai
s̄wạs̄dī
สวัสดี
-
kCFStringTransformHiraganaKatakana
+
kCFStringTransformHiraganaKatakana
にほんご
ニホンゴ
-
kCFStringTransformMandarinLatin
+
kCFStringTransformMandarinLatin
中文
zhōng wén
@@ -112,31 +112,31 @@ One of the more practical applications for string transformation is to normalize
For example, let's say you want to build a searchable index of movies on the device, which includes greetings from around the world:
-~~~{swift}
+```swift
var mutableString = NSMutableString(string: "Hello! こんにちは! สวัสดี! مرحبا! 您好!") as CFMutableStringRef
-~~~
+```
- First, apply the `kCFStringTransformToLatin` transform to transliterate all non-English text into a Latin alphabetic representation.
-~~~{swift}
+```swift
CFStringTransform(mutableString, nil, kCFStringTransformToLatin, Boolean(0))
-~~~
+```
> Hello! こんにちは! สวัสดี! مرحبا! 您好! →
> Hello! kon'nichiha! s̄wạs̄dī! mrḥbạ! nín hǎo!
- Next, apply the `kCFStringTransformStripCombiningMarks` transform to remove any diacritics or accents.
-~~~{swift}
+```swift
CFStringTransform(mutableString, nil, kCFStringTransformStripCombiningMarks, Boolean(0))
-~~~
+```
> Hello! kon'nichiha! s̄wạs̄dī! mrḥbạ! nín hǎo! →
> Hello! kon'nichiha! swasdi! mrhba! nin hao!
- Finally, downcase the text with `CFStringLowercase`, and split the text into tokens with [`CFStringTokenizer`](https://developer.apple.com/library/mac/#documentation/CoreFoundation/Reference/CFStringTokenizerRef/Reference/reference.html) to use as an index for the text.
-~~~{swift}
+```swift
let tokenizer = CFStringTokenizerCreate(nil, mutableString, CFRangeMake(0, CFStringGetLength(mutableString)), 0, CFLocaleCopyCurrent())
var mutableTokens: [String] = []
@@ -147,7 +147,7 @@ do {
let token = CFStringCreateWithSubstring(nil, mutableString, range) as NSString
mutableTokens.append(token)
} while type != .None
-~~~
+```
> (hello, kon'nichiha, swasdi, mrhba, nin, hao)
diff --git a/2012-08-13-nsincrementalstore.md b/2012-08-13-nsincrementalstore.md
index 665c31c2..23bde340 100644
--- a/2012-08-13-nsincrementalstore.md
+++ b/2012-08-13-nsincrementalstore.md
@@ -1,11 +1,12 @@
---
title: NSIncrementalStore
-author: Mattt Thompson
+author: Mattt
category: Cocoa
excerpt: Even for a blog dedicated to obscure APIs, `NSIncrementalStore` sets a new standard. It was introduced in iOS 5, with no more fanfare than the requisite entry in the SDK changelog. Ironically, it is arguably the most important thing to come out of iOS 5, which will completely change the way we build apps from here on out.
+retired: true
status:
- swift: 1.1
- reviewed: September 8, 2015
+ swift: 1.1
+ reviewed: September 8, 2015
---
Even for a blog dedicated to obscure APIs, `NSIncrementalStore` brings a new meaning to the word "obscure".
@@ -24,7 +25,7 @@ And yet, `NSIncrementalStore` is arguably the most important thing to come out o
For those of you not as well-versed in Core Data, here's some background:
-[Core Data](http://developer.apple.com/library/mac/#documentation/cocoa/Conceptual/CoreData/cdProgrammingGuide.html) is Apple's framework for object relational mapping. It's used in at least half of all of the first-party apps on Mac and iOS, as well as thousands of other third-party apps. Core Data is complex, but that's because it solves complex problems, covering a decades-worth of one-offs and edge cases.
+[Core Data](https://developer.apple.com/library/mac/#documentation/cocoa/Conceptual/CoreData/cdProgrammingGuide.html) is Apple's framework for object relational mapping. It's used in at least half of all of the first-party apps on Mac and iOS, as well as thousands of other third-party apps. Core Data is complex, but that's because it solves complex problems, covering a decades-worth of one-offs and edge cases.
This is all to say that Core Data is something you should probably use in your application.
@@ -40,7 +41,7 @@ With `NSIncrementalStore`, developers now have a sanctioned, reasonable means to
`+initialize` is automatically called the first time a class is loaded, so this is a good place to register with `NSPersistentStoreCoordinator`:
-~~~{swift}
+```swift
class CustomIncrementalStore: NSIncrementalStore {
override class func initialize() {
NSPersistentStoreCoordinator.registerStoreClass(self, forStoreType:self.type)
@@ -50,10 +51,9 @@ class CustomIncrementalStore: NSIncrementalStore {
return NSStringFromClass(self)
}
}
-~~~
+```
-
-~~~{objective-c}
+```objc
+ (void)initialize {
[NSPersistentStoreCoordinator registerStoreClass:self forStoreType:[self type]];
}
@@ -61,13 +61,13 @@ class CustomIncrementalStore: NSIncrementalStore {
+ (NSString *)type {
return NSStringFromClass(self);
}
-~~~
+```
### `-loadMetadata:`
`loadMetadata:` is where the incremental store has a chance to configure itself. There is, however, a bit of Kabuki theater boilerplate that's necessary to get everything set up. Specifically, you need to set a UUID for the store, as well as the store type. Here's what that looks like:
-~~~{swift}
+```swift
override func loadMetadata(error: NSErrorPointer) -> Bool {
self.metadata = [
NSStoreUUIDKey: NSProcessInfo().globallyUniqueString,
@@ -76,14 +76,14 @@ override func loadMetadata(error: NSErrorPointer) -> Bool {
return true
}
-~~~
+```
-~~~{objective-c}
+```objc
NSMutableDictionary *mutableMetadata = [NSMutableDictionary dictionary];
[mutableMetadata setValue:[[NSProcessInfo processInfo] globallyUniqueString] forKey:NSStoreUUIDKey];
[mutableMetadata setValue:[[self class] type] forKey:NSStoreTypeKey];
[self setMetadata:mutableMetadata];
-~~~
+```
### `-executeRequest:withContext:error:`
@@ -103,7 +103,7 @@ This method requires very specific and very different return values depending on
- Result Type: `NSCountResultType`
-> **Return**: NSNumberNSArray containing one NSNumber of count of objects matching request
+> **Return**: NSNumberNSArray containing one NSNumber of count of objects matching request
#### Request Type: `NSSaveRequestType`
@@ -135,13 +135,13 @@ Finally, this method is called before `executeRequest:withContext:error:` with a
This usually corresponds with a write to the persistence layer, such as an `INSERT` statement in SQL. If, for example, the row corresponding to the object had an auto-incrementing `id` column, you could generate an objectID with:
-~~~{swift}
+```swift
self.newObjectIDForEntity(entity, referenceObject: rowID)
-~~~
+```
-~~~{objective-c}
+```objc
[self newObjectIDForEntity:entity referenceObject:[NSNumber numberWithUnsignedInteger:rowID]];
-~~~
+```
## Roll Your Own Core Data Backend
@@ -149,16 +149,6 @@ Going through all of the necessary methods to override in an `NSIncrementalStore
What makes `NSIncrementalStore` so exciting is that you _can_ build a store on your favorite technology, and drop that into any existing Core Data stack with little to no additional configuration.
-So imagine if, instead SQL or NoSQL, we wrote a Core Data store that connected to a webservice. Allow me to introduce [AFIncrementalStore](https://github.com/AFNetworking/AFIncrementalStore).
-
-## AFIncrementalStore
-
-[`AFIncrementalStore`](https://github.com/AFNetworking/AFIncrementalStore) is an NSIncrementalStore subclass that uses [AFNetworking](https://github.com/afnetworking/afnetworking) to automatically request resources as properties and relationships are needed.
-
-What this means is that you can now write apps that communicate with a webservice _without exposing any of the details about the underlying API_. Any time a fetch request is made or an attribute or relationship faults, an asynchronous network request will fetch that information from the webservice.
-
-Since the store abstracts all of the implementation details of the API away, you can write expressive fetch requests and object relationships from the start. No matter how bad or incomplete an API may be, you can change all of that mapping independently of the business logic of the client.
-
-* * *
+---
Even though `NSIncrementalStore` has been around since iOS 5, we're still a long way from even beginning to realize its full potential. The future is insanely bright, so you best don your aviators, grab an iced latte and start coding something amazing.
diff --git a/2012-08-27-cfbag.md b/2012-08-27-cfbag.md
index ebad39c5..7869c9ec 100644
--- a/2012-08-27-cfbag.md
+++ b/2012-08-27-cfbag.md
@@ -1,6 +1,6 @@
---
title: CFBag
-author: Mattt Thompson
+author: Mattt
category: Cocoa
tags: nshipster
excerpt: "In the pantheon of collection data types in computer science, bag doesn't really have the same clout as lists, sets, associative arrays, trees, graphs, or priority queues. In fact, it's pretty obscure. You've probably never heard of it."
@@ -10,7 +10,7 @@ status:
Objective-C is a language caught between two worlds.
-On one side, it follows the thoughtful, object-oriented philosophy of [Smalltalk](http://en.wikipedia.org/wiki/Smalltalk), which brings ideas like message sending and named parameters. On the other, are the inescapable vestiges of C, which brings it power and a dash of chaos.
+On one side, it follows the thoughtful, object-oriented philosophy of [Smalltalk](https://en.wikipedia.org/wiki/Smalltalk), which brings ideas like message sending and named parameters. On the other, are the inescapable vestiges of C, which brings it power and a dash of chaos.
It's an identity crisis borne out through an ever-increasing prevalence of the `@` symbol.
@@ -28,58 +28,58 @@ However, toll-free bridging is the exception when it comes to collection classes
-
NSArray*
-
CFArray*
+
NSArray*
+
CFArray*
✓
-
NSCountedSet
-
CFBag*
+
NSCountedSet
+
CFBag*
N/A
-
CFBinaryHeap
+
CFBinaryHeap
N/A
-
CFBitVector*
+
CFBitVector*
-
NSDictionary*
-
CFDictionary*
+
NSDictionary*
+
CFDictionary*
✓
-
NSIndexSet*
+
NSIndexSet*
N/A
-
NSMapTable
+
NSMapTable
N/A
-
NSOrderedSet
+
NSOrderedSet
N/A
-
NSPointerArray
+
NSPointerArray
N/A
-
NSPointerFunctions
+
NSPointerFunctions
N/A
-
NSSet*
-
CFSet*
+
NSSet*
+
CFSet*
✓
@@ -94,11 +94,11 @@ Take a look at the second row, with `NSCountedSet` and `CFBag`. Notice that unli
## Bags, in the Abstract
-In the pantheon of collection data types in computer science, bag doesn't really have the same clout as lists, sets, associative arrays, trees, graphs, or priority queues.
+In the pantheon of collection data types in computer science, bag doesn't really have the same clout as lists, sets, associative arrays, trees, graphs, or priority queues.
In fact, it's pretty obscure. You've probably never heard of it.
-A bag, or [multiset](http://en.wikipedia.org/wiki/Multiset) is a variant of a set, where members can appear more than once. A count is associated with each unique member of the collection, representing the number of times it has been added. Like with sets, order does not matter.
+A bag, or [multiset](https://en.wikipedia.org/wiki/Multiset) is a variant of a set, where members can appear more than once. A count is associated with each unique member of the collection, representing the number of times it has been added. Like with sets, order does not matter.
Its practical applications are... limited, but you'll know one when it comes up. Tallying votes in a general election? Simulating homework problems an intro probability class? Implementing a game of Yahtzee? Bag is your new bicycle!
@@ -108,7 +108,7 @@ As an implementation of the bag data type, `CFBag` and its mutable counterpart,
Although it lacks the object-oriented convenience of `NSCountedSet`, it makes up for it with a number of ways to customize its behavior. When `CFBag` is created, it can be initialized with a number of callbacks, defined by the `CFBagCallBacks` struct, which specify the way values are inserted, removed, and compared:
-~~~{objective-c}
+```objc
struct CFBagCallBacks {
CFIndex version;
CFBagRetainCallBack retain;
@@ -118,7 +118,7 @@ struct CFBagCallBacks {
CFBagHashCallBack hash;
};
typedef struct CFBagCallBacks CFBagCallBacks;
-~~~
+```
- `retain`: callback used to retain values as they're added to the collection
- `release`: callback used to release values as they're removed from the collection
diff --git a/2012-09-03-locale.md b/2012-09-03-locale.md
new file mode 100644
index 00000000..0d23167a
--- /dev/null
+++ b/2012-09-03-locale.md
@@ -0,0 +1,285 @@
+---
+title: Locale
+author: Mattt
+category: Cocoa
+tags: nshipster
+excerpt: >-
+ 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 privelage to travel
+ or to meet people from other places,
+ you may not even be aware that things could _be_ any other way.
+revisions:
+ "2012-09-03": First Publication
+ "2018-11-28": Updated for Swift 4.2
+status:
+ swift: 4.2
+ reviewed: November 28, 2018
+---
+
+> “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.
+
+```swift
+import Foundation
+
+let units = ["meter", "smoot", "agate", "ångström"]
+
+units.sorted { (lhs, rhs) in
+ lhs.compare(rhs, locale: .current) == .orderedAscending
+}
+// => ["agate", "ångström", "meter", "smoot"]
+```
+
+You can also construct a `Locale`
+that corresponds to a particular locale identifier.
+A locale identifier typically comprises
+an ISO 639-1 language code (such as `en` for English) and
+an ISO 3166-2 region code (such as `US` for the United States).
+
+```swift
+let 🇸🇪 = Locale(identifier: "sv_SE")
+units.sorted { (lhs, rhs) in
+ lhs.compare(rhs, locale: 🇸🇪) == .orderedAscending
+}
+// => ["agate", "meter", "smoot", "ångström"]
+```
+
+{% info %}
+
+Most methods and properties that take a `Locale`
+do so optionally or with a default value
+such that ---
+if left unspecified ---
+the current locale is used.
+
+{% endinfo %}
+
+Locale identifiers may also specify
+an explicit character encoding or
+other preferences like currency, calendar system, or number format
+with the following syntax:
+
+```
+language[_region][.character-encoding][@modifier=value[, ...]]
+```
+
+For example,
+the locale identifier `de_DE.UTF8@collation=phonebook,currency=DEM`
+specifies a German language locale in Germany
+that uses UTF-8 text encoding,
+[phonebook collation](http://developer.mimer.com/charts/german_phonebook.htm),
+and the pre-Euro [Deutsche Mark](https://en.wikipedia.org/wiki/Deutsche_Mark) currency.
+
+{% warning %}
+
+Support for identifiers specifying other than language or region is inconsistent
+across system APIs and different platforms,
+so you shouldn't rely on specific behavior.
+
+{% endwarning %}
+
+Users can change their locale settings
+in the "Language & Text" system preference on macOS and
+in "General > International" in the iOS Settings app.
+
+{% asset locale-preferences.png alt="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:
+
+{::nomarkdown}
+
+
+
+{:/}
+
+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...
+
+```swift
+import Foundation
+
+let 🇺🇸 = 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:
+
+{% asset locale-languages.png alt="Locale Languages Menu on iOS" %}
+
+{% info %}
+
+Behind the scenes,
+all of this locale information is provided by the
+[Common Locale Data Repository](http://cldr.unicode.org)
+CLDR,
+a companion project to the Unicode standard.
+
+{% endinfo %}
+
+## _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](http://tools.ietf.org/html/bcp47)
+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](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4)
+to give the server an opportunity to return localized resources:
+
+```swift
+import Foundation
+
+let url = URL(string: "https://nshipster.com")!
+var request = URLRequest(url: url)
+
+let acceptLanguage = Locale.preferredLanguages.joined(separator: ", ")
+request.setValue(acceptLanguage, forHTTPHeaderField: "Accept-Language")
+```
+
+Even if your server doesn't yet localize its resources,
+putting this in place now allows you to flip the switch when the time comes ---
+without having to push an update to the client.
+_Neat!_
+
+{% info %}
+[HTTP content negotiation](https://tools.ietf.org/html/rfc7231#section-3.4)
+is a complex topic,
+and apps interacting with localized, remote resources
+should use `Locale.preferredLanguages` as a starting point
+for generating a more precise `Accept-Language` field value.
+For example,
+you might specify an associated quality value
+like in `en-SG, en;q=0.9`
+to tell the server
+_"I prefer Singaporean English, but will accept other types of English"_.
+
+For more information,
+see [RFC 7231](https://tools.ietf.org/html/rfc7231#section-5.3.5).
+{% endinfo %}
+
+---
+
+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.
diff --git a/2012-09-03-nslocale.md b/2012-09-03-nslocale.md
deleted file mode 100644
index 50bf828b..00000000
--- a/2012-09-03-nslocale.md
+++ /dev/null
@@ -1,157 +0,0 @@
----
-title: NSLocale
-author: Mattt Thompson
-category: Cocoa
-tags: nshipster
-excerpt: "Internationalization is like flossing: everyone knows they should do it, but probably don't."
-status:
- swift: 1.1
----
-
-Internationalization is like flossing: everyone knows they should do it, but probably don't.
-
-And like any habit, it becomes second-nature with practice, to the point that you couldn't imagine _not_ doing it. All it takes is for someone to show you the way.
-
-Let NSHipster be your dental hygienist Virgil through these foreign lands.. without all of the lecturing about tooth decay (promsies!)
-
-## i18n versus l10n
-
-As is necessary in any discussion about Internationalization (i18n) or Localization (l10n), we must take some time to differentiate the two:
-
-- **Localization** is the process of adapting your application for a specific market, or _locale_.
-- **Internationalization** is the process of preparing your app to be localized.
-
-> Internationalization is a necessary, but not sufficient condition for localization, and will be the focus of this article. Localization, which involves the translation of text and assets into a particular language, will be covered in a future edition of NSHipster.
-
-What makes internationalization difficult is having to think outside of your cultural context. All of the assumptions you have about the way things are supposed to work must be acknowledged and reconsidered. You have to fight the urge to write off things that may seem trivial, like sorting and collation, and empathize with the pain and confusion even minor differences may cause.
-
-Fortunately for us, we don't have to do this alone. Meet `NSLocale`:
-
-## `NSLocale`
-
-`NSLocale` is a Foundation class that encapsulates all of the conventions about language and culture for a particular locale. A locale encompasses all of the linguistic and cultural norms of a particular group of people, including:
-
-- Language
-- Keyboards
-- Number, Date, and Time Formats
-- Currency
-- Collation and Sorting
-- Use of Symbols, Colors, and Iconography
-
-Each locale corresponds to a _locale identifier_, such as `en_US`, `fr_FR`, `ja_JP`, and `en_GB`, which include a language code (e.g. `en` for English) and a region code (e.g. `US` for United States).
-
-Locale identifiers can encode more explicit preferences about currency, calendar system, or number formats, such as in the case of `de_DE@collation=phonebook,currency=DDM`, which specifies German spoken in Germany, using [phonebook collation](http://developer.mimer.com/charts/german_phonebook.htm), and using the pre-Euro [Deutsche Mark](http://en.wikipedia.org/wiki/Deutsche_Mark).
-
-Users can change their locale settings in the "Language & Text" (or "International" on older versions of OS X) System Preferences on the Mac, or "General > International" in iOS Settings.
-
-
-
-## Formatting Dates & Numbers
-
-Although `NSLocale` encapsulates a rich set of domain-specific information, its typical usage is rather understated.
-
-If there's just one thing you should learn about `NSLocale`, it's that you should always pass `[NSLocale currentLocale]` into your `NSDateFormatter` and `NSNumberFormatter` instances. Doing this will ensure that dates, numbers, and currencies will be formatted according to the localization preferences of the user.
-
-Actually, make that a meta lesson about locales: always use `NSDateFormatter` and `NSNumberFormatter` when displaying anything to do with dates or numbers, respectively.
-
-But let's get back to some of the cool features of `NSLocale` itself, shall we?
-
-## `-objectForKey:`
-
-`NSLocale` typifies Foundation's obsession with domain-specific pedantry, and nowhere is this more visible than in `-objectForKey:`. Cue the list of available constants:
-
-- `NSLocaleIdentifier`
-- `NSLocaleLanguageCode`
-- `NSLocaleCountryCode`
-- `NSLocaleScriptCode`
-- `NSLocaleVariantCode`
-- `NSLocaleExemplarCharacterSet`
-- `NSLocaleCalendar`
-- `NSLocaleCollationIdentifier`
-- `NSLocaleUsesMetricSystem`
-- `NSLocaleMeasurementSystem`
-- `NSLocaleDecimalSeparator`
-- `NSLocaleGroupingSeparator`
-- `NSLocaleCurrencySymbol`
-- `NSLocaleCurrencyCode`
-- `NSLocaleCollatorIdentifier`
-- `NSLocaleQuotationBeginDelimiterKey`
-- `NSLocaleQuotationEndDelimiterKey`
-- `NSLocaleAlternateQuotationBeginDelimiterKey`
-- `NSLocaleAlternateQuotationEndDelimiterKey`
-
-While this all may seem like fairly esoteric stuff, you may be surprised by the number of opportunities your application has to use this information to make for a better user experience.
-
-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 `NSLocaleQuotationBeginDelimiterKey` and `NSLocaleAlternateQuotationEndDelimiterKey` rather than assuming `@"\""` for English quotation marks.
-
-## `-displayNameForKey:value:`
-
-Another impressive, albeit mostly-useless method is `-displayNameForKey:value:`, which can return the display name of a locale identifier (`NSLocaleIdentifier`):
-
-~~~{swift}
-let locale = NSLocale(localeIdentifier: "fr_FR")
-
-let fr_FR = locale.displayNameForKey(NSLocaleIdentifier, value: "fr_FR")
-let en_US = locale.displayNameForKey(NSLocaleIdentifier, value: "en_US")
-~~~
-
-~~~{objective-c}
-NSLocale *frLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"fr_FR"];
-NSLog(@"fr_FR: %@", [frLocale displayNameForKey:NSLocaleIdentifier value:@"fr_FR"]);
-NSLog(@"en_US: %@", [frLocale displayNameForKey:NSLocaleIdentifier value:@"en_US"]);
-~~~
-
-- `fr_FR`: "français (France)"
-- `en_US`: "anglais (États-Unis)"
-
-You should use this method any time you need to display information about the user's current locale, or any alternative locales available to them, like in this screen from the Settings app:
-
-
-
-## `+preferredLanguages`
-
-One final method worth mentioning is `NSLocale +preferredLanguages`, which returns an array of [IETF BCP 47 language identifier](http://tools.ietf.org/html/bcp47) strings, in order of user preference.
-
-An app that communicates with a web server can use these values to define the `Accept-Language` HTTP header, such that the server has the option to return localized resources:
-
-~~~{swift}
-// Accept-Language HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
-let acceptLanguage: String = {
- var components: [String] = []
- for (index, languageCode) in enumerate(NSLocale.preferredLanguages() as [String]) {
- let q = 1.0 - (Double(index) * 0.1)
- components.append("\(languageCode);q=\(q)")
- if q <= 0.5 {
- break
- }
- }
-
- return join(",", components)
-}()
-
-let URL = NSURL(string: "http://nshipster.com")
-var mutableRequest = NSMutableURLRequest(URL: URL)
-
-mutableRequest.setValue(acceptLanguage, forHTTPHeaderField: "Accept-Language")
-~~~
-
-~~~{objective-c}
-NSMutableURLRequest *request = ...;
-[request setValue:[NSString stringWithFormat:@"%@", [[NSLocale preferredLanguages] componentsJoinedByString:@", "]], forHTTPHeaderField:@"Accept-Language"];
-~~~
-
-Even if your server doesn't yet localize its resources, putting this in place now will allow you to flip the switch when the time comes, without having to push an update to the client. Neat!
-
----
-
-Internationalization is often considered to be an un-sexy topic in programming--just another chore that most projects don't have to worry about. In actuality, designing software for other locales is a valuable exercise (and not just for the economic benefits of expanding your software into other markets).
-
-One of the greatest joys and challenges in programming is in designing systems that can withstand change. The only way designs can survive this level of change is to identify and refactor assumptions about the system that may not always hold. In this way, internationalization represents the greatest challenge, making us question everything about our cultural identity. And in doing so, we become not just better programmers, but better people, too.
-
-So go and be a better person: make `NSLocale` part of your daily ritual.
diff --git a/2012-09-10-uiaccessibility.md b/2012-09-10-uiaccessibility.md
index 79393d56..eef60f1b 100644
--- a/2012-09-10-uiaccessibility.md
+++ b/2012-09-10-uiaccessibility.md
@@ -1,6 +1,6 @@
---
title: UIAccessibility
-author: Mattt Thompson
+author: Mattt
category: Cocoa
tags: nshipster
excerpt: "Accessibility, like internationalization, is one of those topics that's difficult to get developers excited about. But as you know, NSHipster is all about getting developers excited about this kind of stuff."
@@ -9,13 +9,14 @@ status:
---
> We all want to help one another, human beings are like that.
+>
> - [Charlie Chaplin](http://en.wikiquote.org/wiki/Charlie_Chaplin)
You know what I wish everyone would copy from Apple? Their assistive technologies.
-iPhones and iPads--magical as they are--become downright _life-changing_ for individuals with disabilities and their families because of Apple's commitment to accessibility. Look no further than the [WWDC 2012 Introduction Video](http://www.youtube.com/watch?v=MbP_pxR5cMk), which opens with Per Busch, a blind man who walks the woods of Kassel, Germany with the aid of [Ariadne GPS](http://www.ariadnegps.eu). It's a lovely reminder of the kind of impact our work can have on others.
+iPhones and iPads--magical as they are--become downright _life-changing_ for individuals with disabilities and their families because of Apple's commitment to accessibility. Look no further than the [WWDC 2012 Introduction Video](https://www.youtube.com/watch?v=MbP_pxR5cMk), which opens with Per Busch, a blind man who walks the woods of Kassel, Germany with the aid of [Ariadne GPS](http://www.ariadnegps.eu). It's a lovely reminder of the kind of impact our work can have on others.
-Accessibility, like [internationalization](http://nshipster.com/nslocale/), is one of those topics that's difficult to get developers excited about. But as you know, NSHipster is _all about_ getting developers excited about this kind of stuff. Let's get started:
+Accessibility, like [internationalization](https://nshipster.com/nslocale/), is one of those topics that's difficult to get developers excited about. But as you know, NSHipster is _all about_ getting developers excited about this kind of stuff. Let's get started:
---
@@ -43,7 +44,7 @@ Tap VoiceOver, and then tap the VoiceOver switch to turn it on. An alert will po
Don't Panic--unlike setting your device to another language, there's no real risk of not being able to figure out how to turn VoiceOver off.
-
+
Using the device in VoiceOver mode is a bit different than you're used to:
@@ -68,20 +69,20 @@ Accessibility labels and hints tell VoiceOver what to say when selecting user in
- **`accessibilityLabel`** identifies a user interface element. Every accessible view and control _must_ supply a label.
- **`accessibilityHint`** describes the results of interacting with a user interface element. A hint should be supplied _only_ if the result of an interaction is not obvious from the element's label.
-The [Accessibility Programming Guide](http://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/iPhoneAccessibility/Making_Application_Accessible/Making_Application_Accessible.html) provides the following guidelines for labels and hints:
+The [Accessibility Programming Guide](https://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/iPhoneAccessibility/Making_Application_Accessible/Making_Application_Accessible.html) provides the following guidelines for labels and hints:
> ### Guidelines for Creating Labels
-> If you provide a custom control or view, or if you display a custom icon in a standard control or view, you need to provide a label that:
>
+> If you provide a custom control or view, or if you display a custom icon in a standard control or view, you need to provide a label that:
+
- **Very briefly describes the element.** Ideally, the label consists of a single word, such as Add, Play, Delete, Search, Favorites, or Volume.
- **Does not include the type of the control or view.** The type information is contained in the traits attribute of the element and should never be repeated in the label.
- **Begins with a capitalized word.** This helps VoiceOver read the label with the appropriate inflection.
- **Does not end with a period.** The label is not a sentence and therefore should not end with a period.
- **Is localized.** Be sure to make your application available to as wide an audience as possible by localizing all strings, including accessibility attribute strings. In general, VoiceOver speaks in the language that the user specifies in International settings.
->
-> ### Guidelines for Creating Hints
-> The hint attribute describes the results of performing an action on a control or view. You should provide a hint only when the results of an action are not obvious from the element’s label.
->
+ > ### Guidelines for Creating Hints
+ >
+ > The hint attribute describes the results of performing an action on a control or view. You should provide a hint only when the results of an action are not obvious from the element’s label.
- **Very briefly describes the results.** Even though few controls and views need hints, strive to make the hints you do need to provide as brief as possible. Doing so decreases the amount of time users must spend listening before they can use the element.
- **Begins with a verb and omits the subject.** Be sure to use the third-person singular declarative form of a verb, such as “Plays,” and not the imperative, such as “Play.” You want to avoid using the imperative, because using it can make the hint sound like a command.
- **Begins with a capitalized word and ends with a period.** Even though a hint is a phrase, not a sentence, ending the hint with a period helps VoiceOver speak it with the appropriate inflection.
@@ -133,6 +134,6 @@ Want to know a quick way to improve the accessibility of your table views? Try s
Apple has done a great service to humanity in making accessibility a first-class citizen in its hardware and software. You're missing out on some of the best engineering, design, and technical writing that Apple has ever done if you ignore `UIAccessibility`.
-Do yourself a favor and read the _excellent_ [Accessibility Programming Guide for iOS](http://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/iPhoneAccessibility/Introduction/Introduction.html). It only takes an hour or two to get the hang of everything.
+Do yourself a favor and read the _excellent_ [Accessibility Programming Guide for iOS](https://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/iPhoneAccessibility/Introduction/Introduction.html). It only takes an hour or two to get the hang of everything.
Who knows? You may end up changing someone's life because of it.
diff --git a/2012-09-17-characterset.md b/2012-09-17-characterset.md
new file mode 100644
index 00000000..71593f41
--- /dev/null
+++ b/2012-09-17-characterset.md
@@ -0,0 +1,350 @@
+---
+title: CharacterSet
+author: Mattt
+category: Cocoa
+excerpt: >-
+ `CharacterSet` isn't a set and it doesn't contain `Character` values.
+ Before we use it to trim, filter, and search through text,
+ we should take a closer look to see what's actually going on.
+revisions:
+ "2012-09-17": Original publication
+ "2018-12-12": Updated for Swift 4.2
+status:
+ swift: 4.2
+ reviewed: December 12, 2018
+---
+
+In Japan,
+there's a comedy tradition known as
+[Manzai (漫才)](https://en.wikipedia.org/wiki/Manzai).
+It's kind of a cross between stand up and vaudeville,
+with a straight man and a funny man
+delivering rapid-fire jokes that revolve around miscommunication and wordplay.
+
+As it were, we've been working on a new routine
+as a way to introduce the subject for this week's article, `CharacterSet`,
+and wanted to see what you thought:
+
+{::nomarkdown}
+
+
+
+
+Is CharacterSet a Set<Character>?
+
+
+
+
+Of course not!
+
+
+
+
+What about NSCharacterSet?
+
+
+
+
+That's an old reference.
+
+
+
+
+Then what do you call a collection of characters?
+
+
+
+
+That would be a String!
+
+
+
+
+(╯° 益 °)╯ 彡 ┻━┻
+
+
+
+
+{:/}
+
+_(Yeah, we might need to workshop this one a bit more.)_
+
+All kidding aside,
+`CharacterSet` is indeed ripe for miscommunication and wordplay (so to speak):
+it doesn't store `Character` values,
+and it's not a `Set` in the literal sense.
+
+So what is `CharacterSet` and how can we use it?
+Let's find out! (行きましょう!)
+
+---
+
+`CharacterSet` (and its reference type counterpart, `NSCharacterSet`)
+is a Foundation type used to trim, filter, and search for
+characters in text.
+
+In Swift,
+a `Character` is an extended grapheme cluster
+(really just a `String` with a length of 1)
+that comprises one or more scalar values.
+`CharacterSet` stores those underlying `Unicode.Scalar` values,
+rather than `Character` values, as the name might imply.
+
+The "set" part of `CharacterSet`
+refers not to `Set` from the Swift standard library,
+but instead to the `SetAlgebra` protocol,
+which bestows the type with the same interface:
+`contains(_:)`, `insert(_:)`, `union(_:)`, `intersection(_:)`, and so on.
+
+## Predefined Character Sets
+
+`CharacterSet` defines constants
+for sets of characters that you're likely to work with,
+such as letters, numbers, punctuation, and whitespace.
+Most of them are self-explanatory and,
+with only a few exceptions,
+correspond to one or more
+[Unicode General Categories](https://unicode.org/reports/tr44/#General_Category_Values).
+
+| Type Property | Unicode General Categories & Code Points |
+| --------------------------------- | ---------------------------------------- |
+| `alphanumerics` | L\*, M\*, N\* |
+| `letters` | L\*, M\* |
+| `capitalizedLetters`\* | Lt |
+| `lowercaseLetters` | Ll |
+| `uppercaseLetters` | Lu, Lt |
+| `nonBaseCharacters` | M\* |
+| `decimalDigits` | Nd |
+| `punctuationCharacters` | P\* |
+| `symbols` | S\* |
+| `whitespaces` | Zs, U+0009 |
+| `newlines` | U+000A – U+000D, U+0085, U+2028, U+2029 |
+| `whitespacesAndNewlines` | Z\*, U+000A – U+000D, U+0085 |
+| `controlCharacters` | Cc, Cf |
+| `illegalCharacters` | Cn |
+
+{% info %}
+A common mistake is to use `capitalizedLetters`
+when what you actually want is `uppercaseLetters`.
+Unicode actually defines three cases:
+lowercase, uppercase, and titlecase.
+You can see this in the Latin script used for
+Serbo-Croatian and other South Slavic languages,
+in which digraphs like "dž" are considered single letters,
+and have separate forms for
+lowercase (dž), uppercase (DŽ), and titlecase (Dž).
+The `capitalizedLetters` character set contains only
+a few dozen of those titlecase digraphs.
+{% endinfo %}
+
+The remaining predefined character set, `decomposables`,
+is derived from the
+[decomposition type and mapping](https://unicode.org/reports/tr44/#Character_Decomposition_Mappings)
+of characters.
+
+### Trimming Leading and Trailing Whitespace
+
+Perhaps the most common use for `CharacterSet`
+is to remove leading and trailing whitespace from text.
+
+```swift
+"""
+
+ 😴
+
+""".trimmingCharacters(in: .whitespacesAndNewlines) // "😴"
+```
+
+You can use this, for example,
+when sanitizing user input or preprocessing text.
+
+## Predefined URL Component Character Sets
+
+In addition to the aforementioned constants,
+`CharacterSet` provides predefined values
+that correspond to the characters allowed in various
+[components of a URL](https://nshipster.com/nsurl/):
+
+- `urlUserAllowed`
+- `urlPasswordAllowed`
+- `urlHostAllowed`
+- `urlPathAllowed`
+- `urlQueryAllowed`
+- `urlFragmentAllowed`
+
+### Escaping Special Characters in URLs
+
+Only certain characters are allowed in certain parts of a URL
+without first being escaped.
+For example, spaces must be percent-encoded as `%20` (or `+`)
+when part of a query string like
+`https://nshipster.com/search/?q=character%20set`.
+
+`URLComponents` takes care of percent-encoding components automatically,
+but you can replicate this functionality yourself
+using the `addingPercentEncoding(withAllowedCharacters:)` method
+and passing the appropriate character set:
+
+```swift
+let query = "character set"
+query.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
+// "character%20set"
+```
+
+{% warning %}
+[Internationalized domain names](https://en.wikipedia.org/wiki/Internationalized_domain_name)
+encode non-ASCII characters using
+[Punycode](https://en.wikipedia.org/wiki/Punycode)
+instead of percent-encoding
+(for example,
+[NSHipster.中国](https://nshipster.cn) would be
+[NSHipster.xn--fiqy6j](https://nshipster.cn))
+Punycode encoding / decoding isn't currently provided by Apple SDKs.
+{% endwarning %}
+
+## Building Your Own
+
+In addition to these predefined character sets,
+you can create your own.
+Build them up character by character,
+inserting multiple characters at a time by passing a string,
+or by mixing and matching any of the predefined sets.
+
+### Validating User Input
+
+You might create a `CharacterSet` to validate some user input to, for example,
+allow only lowercase and uppercase letters, digits, and certain punctuation.
+
+```swift
+var allowed = CharacterSet()
+allowed.formUnion(.lowercaseLetters)
+allowed.formUnion(.uppercaseLetters)
+allowed.formUnion(.decimalDigits)
+allowed.insert(charactersIn: "!@#$%&")
+
+func validate(_ input: String) -> Bool {
+ return input.unicodeScalars.allSatisfy { allowed.contains($0) }
+}
+```
+
+Depending on your use case,
+you might find it easier to think in terms of what shouldn't be allowed,
+in which case you can compute the inverse character set
+using the `inverted` property:
+
+```swift
+let disallowed = allowed.inverted
+func validate(_ input: String) -> Bool {
+ return input.rangeOfCharacter(from: disallowed) == nil
+}
+```
+
+### Caching Character Sets
+
+If a `CharacterSet` is created as the result of an expensive operation,
+you may consider caching its `bitmapRepresentation`
+for later reuse.
+
+For example,
+if you wanted to create `CharacterSet` for Emoji,
+you might do so by enumerating over the Unicode code space (U+0000 – U+1F0000)
+and inserting the scalar values for any characters with
+[Emoji properties](https://www.unicode.org/reports/tr51/#Emoji_Properties)
+using the `properties` property added in Swift 5 by
+[SE-0221 "Character Properties"](https://github.com/apple/swift-evolution/blob/master/proposals/0221-character-properties.md):
+
+```swift
+import Foundation
+
+var emoji = CharacterSet()
+
+for codePoint in 0x0000...0x1F0000 {
+ guard let scalarValue = Unicode.Scalar(codePoint) else {
+ continue
+ }
+
+ // Implemented in Swift 5 (SE-0221)
+ // https://github.com/apple/swift-evolution/blob/master/proposals/0221-character-properties.md
+ if scalarValue.properties.isEmoji {
+ emoji.insert(scalarValue)
+ }
+}
+```
+
+The resulting `bitmapRepresentation` is a 16KB `Data` object.
+
+```swift
+emoji.bitmapRepresentation // 16385 bytes
+```
+
+You could store that in a file somewhere in your app bundle,
+or embed its [Base64 encoding](https://en.wikipedia.org/wiki/Base64)
+as a string literal directly in the source code itself.
+
+```swift
+extension CharacterSet {
+ static var emoji: CharacterSet {
+ let base64Encoded = """
+ AAAAAAgE/wMAAAAAAAAAAAAAAAAA...
+ """
+ let data = Data(base64Encoded: base64Encoded)!
+
+ return CharacterSet(bitmapRepresentation: data)
+ }
+}
+
+CharacterSet.emoji.contains("👺") // true
+```
+
+{% info %}
+Because the Unicode code space is a closed range,
+`CharacterSet` can express the membership of a given scalar value
+using a single bit in a [bit map](https://en.wikipedia.org/wiki/Bit_array),
+rather than using a
+[universal hashing function](https://en.wikipedia.org/wiki/Universal_hashing)
+like a conventional `Set`.
+On top of that, `CharacterSet` does some clever optimizations, like
+allocating on a per-[plane](https://www.unicode.org/glossary/#plane) basis
+and representing sets of contiguous scalar values as ranges, if possible.
+{% endinfo %}
+
+---
+
+Much like our attempt at a Manzai routine at the top of the article,
+some of the meaning behind `CharacterSet` is lost in translation.
+
+`NSCharacterSet` was designed for `NSString`
+at a time when characters were equivalent to 16-bit UCS-2 code units
+and text rarely had occasion to leave the Basic Multilingual Plane.
+But with Swift's modern,
+Unicode-compliant implementations of `String` and `Character`,
+the definition of terms has drifted slightly;
+along with its `NS` prefix,
+`CharacterSet` lost some essential understanding along the way.
+
+Nevertheless,
+`CharacterSet` remains a performant, specialized container type
+for working with collections of scalar values.
+
+
+FIN
+
+
+
+{% asset articles/characterset.css %}
diff --git a/2012-09-17-nscharacterset.md b/2012-09-17-nscharacterset.md
deleted file mode 100644
index 289f53cd..00000000
--- a/2012-09-17-nscharacterset.md
+++ /dev/null
@@ -1,177 +0,0 @@
----
-title: NSCharacterSet
-author: Mattt Thompson
-category: Cocoa
-excerpt: "Foundation boasts one of the best, most complete implementations of strings around. But a string implementation is only as good as the programmer who wields it. So this week, we're going to explore some common uses--and misuses--of an important part of the Foundation string ecosystem: NSCharacterSet."
-status:
- swift: 2.0
- reviewed: September 9, 2015
----
-
-As mentioned [previously](http://nshipster.com/cfstringtransform/), Foundation boasts one of the best, most complete implementations of strings around.
-
-But a string implementation is only as good as the programmer who wields it. So this week, we're going to explore some common uses--and misuses--of an important part of the Foundation string ecosystem: `NSCharacterSet`.
-
----
-
-> If you're fuzzy on what character encodings are (or even if you have a pretty good working knowledge), you should take this opportunity to read / re-read / skim and read later Joel Spolsky's classic essay ["The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)"](http://www.joelonsoftware.com/articles/Unicode.html). Having that fresh in your mind will give you a much better appreciation of everything we're about to cover.
-
-`NSCharacterSet` and its mutable counterpart, `NSMutableCharacterSet`, provide an object-oriented way of representing sets of Unicode characters. It's most often used with `NSString` & `NSScanner` to filter, remove, or split on different kinds of characters. To give you an idea of what those kinds of characters can be, take a look at the class methods provided by `NSCharacterSet`:
-
-- `alphanumericCharacterSet`
-- `capitalizedLetterCharacterSet`
-- `controlCharacterSet`
-- `decimalDigitCharacterSet`
-- `decomposableCharacterSet`
-- `illegalCharacterSet`
-- `letterCharacterSet`
-- `lowercaseLetterCharacterSet`
-- `newlineCharacterSet`
-- `nonBaseCharacterSet`
-- `punctuationCharacterSet`
-- `symbolCharacterSet`
-- `uppercaseLetterCharacterSet`
-- `whitespaceAndNewlineCharacterSet`
-- `whitespaceCharacterSet`
-
-Contrary to what its name might suggest, `NSCharacterSet` has _nothing_ to do with `NSSet`.
-
-However, `NSCharacterSet` _does_ have quite a bit in common with `NSIndexSet`, conceptually if not also in its underlying implementation. `NSIndexSet`, covered [previously](http://nshipster.com/nsindexset/), represents a sorted collection of unique unsigned integers. Unicode characters are likewise unique unsigned integers that roughly correspond to some orthographic representation. Thus, a character set like `NSCharacterSet +lowercaseCharacterSet` is analogous to the `NSIndexSet` of the integers 97 to 122.
-
-Now that we're comfortable with the basic concepts of `NSCharacterSet`, let's see some of those patterns and anti-patterns:
-
-## Stripping Whitespace
-
-`NSString -stringByTrimmingCharactersInSet:` is a method you should know by heart. It's most often passed `NSCharacterSet +whitespaceCharacterSet` or `+whitespaceAndNewlineCharacterSet` in order to remove the leading and trailing whitespace of string input.
-
-It's important to note that this method _only_ strips the _first_ and _last_ contiguous sequences of characters in the specified set. That is to say, if you want to remove excess whitespace between words, you need to go a step further.
-
-## Squashing Whitespace
-
-So let's say you do want to get rid of excessive inter-word spacing for that string you just stripped of whitespace. Here's a really easy way to do that:
-
-~~~{swift}
-var string = " Lorem ipsum dolar sit amet. "
-
-let components = string.componentsSeparatedByCharactersInSet(NSCharacterSet.whitespaceCharacterSet()).filter { !$0.isEmpty }
-
-string = components.joinWithSeparator(" ")
-~~~
-
-~~~{objective-c}
-NSString *string = @"Lorem ipsum dolar sit amet.";
-string = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
-
-NSArray *components = [string componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
-components = [components filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"self <> ''"]];
-
-string = [components componentsJoinedByString:@" "];
-~~~
-
-First, trim the string of leading and trailing whitespace. Next, use `NSString -componentsSeparatedByCharactersInSet:` to split on the remaining whitespace to create an `NSArray`. Next, filter out the blank string components with an `NSPredicate`. Finally, use `NSArray -componentsJoinedByString:` to re-join the components with a single space. Note that this only works for languages like English that delimit words with whitespace.
-
-And now for the anti-patterns. Take a gander at [the answers to this question on StackOverflow](http://stackoverflow.com/questions/758212/how-can-i-strip-all-the-whitespaces-from-a-string-in-objective-c).
-
-At the time of writing, the correct answer ranks second by number of votes, with 58 up and 2 down. The top answer edges it out with 84 up and 24 down.
-
-Now, it's not uncommon for the top-voted / accepted answer to not be the correct one, but this question may set records for number of completely distinct answers (10), and number of unique, completely incorrect answers (9).
-
-Without further ado, here are the 9 _incorrect_ answers:
-
-- "Use `stringByTrimmingCharactersInSet`" - _Only strips the leading and trailing whitespace, as you know._
-- "Replace ' ' with ''" - _This removes **all** of the spaces. Swing and a miss._
-- "Use a regular expression" - _Kinda works, except it doesn't handle leading and trailing whitespace. A regular expression is overkill anyway._
-- "Use Regexp Lite" - _No seriously, regular expressions are completely unnecessary. And it's definitely not worth the external dependency._
-- "Use OgreKit" - _Ditto any other third-party regexp library._
-- "Split the string into components, iterate over them to find components with non-zero length, and then re-combine" - _So close, but `componentsSeparatedByCharactersInSet:` already makes the iteration unnecessary._
-- "Replace two-space strings with single-space strings in a while loop" - _Wrong and oh-so computationally wasteful_.
-- "Manually iterate over each `unichar` in the string and use `NSCharacterSet -characterIsMember:`" - _Shows a surprising level of sophistication for missing the method that does this in the standard library._
-- "Find and remove all of the tabs" - _Thanks all the same, but who said anything about tabs?_
-
-I don't mean to rag on any of the answerers personally--this is all to point out how many ways there are to approach these kinds of tasks, and how many of those ways are totally wrong.
-
-## String Tokenization
-
-**Do not use `NSCharacterSet` to tokenize strings.**
-**Use `CFStringTokenizer` instead.**
-
-You can be forgiven for using `componentsSeparatedByCharactersInSet:` to clean up user input, but do this for anything more complex, and you'll be in a world of pain.
-
-Why? Well, remember that bit about languages not always having whitespace word boundaries? As it turns out, those languages are rather widely used. Just Chinese and Japanese--#1 and #9 in terms of number of speakers, respectively--alone account for 16% of the world population, or well over a billion people.
-
-...and even for languages that do have whitespace word boundaries, tokenization has some obscure edge cases, particularly with compound words and punctuation.
-
-This is all to say: use `CFStringTokenizer` (or `enumerateSubstringsInRange:options:usingBlock:`) if you ever intend to split a string by words in any meaningful way.
-
-## Parse Data From Strings
-
-`NSScanner` is a class that helps to parse data out of arbitrary or semi-structured strings. When you create a scanner for a string, you can specify a set of characters to skip, thus preventing any of those characters from somehow being included in the values parsed from the string.
-
-For example, let's say you have a string that parses opening hours in the following form:
-
-~~~
-Mon-Thurs: 8:00 - 18:00
-Fri: 7:00 - 17:00
-Sat-Sun: 10:00 - 15:00
-~~~
-
-You might `enumerateLinesUsingBlock:` and parse with an `NSScanner` like so:
-
-~~~{swift}
-let skippedCharacters = NSMutableCharacterSet.punctuationCharacterSet()
-skippedCharacters.formUnionWithCharacterSet(NSCharacterSet.whitespaceCharacterSet())
-
-hours.enumerateLines { (line, _) in
- let scanner = NSScanner(string: line)
- scanner.charactersToBeSkipped = skippedCharacters
-
- var startDay, endDay: NSString?
- var startHour: Int = 0
- var startMinute: Int = 0
- var endHour: Int = 0
- var endMinute: Int = 0
-
- scanner.scanCharactersFromSet(NSCharacterSet.letterCharacterSet(), intoString: &startDay)
- scanner.scanCharactersFromSet(NSCharacterSet.letterCharacterSet(), intoString: &endDay)
-
- scanner.scanInteger(&startHour)
- scanner.scanInteger(&startMinute)
- scanner.scanInteger(&endHour)
- scanner.scanInteger(&endMinute)
-}
-~~~
-
-~~~{objective-c}
-NSMutableCharacterSet *skippedCharacters = [NSMutableCharacterSet punctuationCharacterSet];
-[skippedCharacters formUnionWithCharacterSet:[NSCharacterSet whitespaceCharacterSet]];
-
-[hours enumerateLinesUsingBlock:^(NSString *line, BOOL *stop) {
- NSScanner *scanner = [NSScanner scannerWithString:line];
- [scanner setCharactersToBeSkipped:skippedCharacters];
-
- NSString *startDay, *endDay;
- NSUInteger startHour, startMinute, endHour, endMinute;
-
- [scanner scanCharactersFromSet:[NSCharacterSet letterCharacterSet] intoString:&startDay];
- [scanner scanCharactersFromSet:[NSCharacterSet letterCharacterSet] intoString:&endDay];
-
- [scanner scanInteger:&startHour];
- [scanner scanInteger:&startMinute];
- [scanner scanInteger:&endHour];
- [scanner scanInteger:&endMinute];
-}];
-~~~
-
-We first construct an `NSMutableCharacterSet` from the union of whitespace and punctuation characters. Telling `NSScanner` to skip these characters greatly reduces the logic necessary to parse values from the string.
-
-`scanCharactersFromSet:` with the letters character set captures the start and (optional) end day of the week for each entry. `scanInteger` similarly captures the next contiguous integer value.
-
-`NSCharacterSet` and `NSScanner` allow you to code quickly and confidently. They're really a great combination, those two.
-
----
-
-`NSCharacterSet` is but one piece to the Foundation string ecosystem, and perhaps the most misused and misunderstood of them all. By keeping these patterns and anti-patterns in mind, however, not only will you be able to do useful things like manage whitespace and scan information from strings, but--more importantly--you'll be able to avoid all of the wrong ways to do it.
-
-And if not being wrong isn't the most important thing about being an NSHipster, then I don't want to be right!
-
-> Ed. Speaking of (not) being wrong, the original version of this article contained errors in both code samples. These have since been corrected.
diff --git a/2012-09-24-uicollectionview.md b/2012-09-24-uicollectionview.md
index ec04b9e3..3df5e333 100644
--- a/2012-09-24-uicollectionview.md
+++ b/2012-09-24-uicollectionview.md
@@ -1,6 +1,6 @@
---
title: UICollectionView
-author: Mattt Thompson
+author: Mattt
category: Cocoa
excerpt: "UICollectionView single-handedly changes the way we will design and develop iOS apps from here on out. This is not to say that collection views are in any way unknown or obscure. But being an NSHipster isn't just about knowing obscure gems in the rough. Sometimes, it's about knowing about up-and-comers before they become popular and sell out."
status:
@@ -28,19 +28,19 @@ In another departure from the old-school table view way of doing things, the pro
In `-tableView:cellForRowAtIndexPath:`, a developer had to invoke the familiar incantation:
-~~~{swift}
+```swift
let identifier = "Cell"
var cell = tableView.dequeueReusableCellWithIdentifier(identifier)
if cell == nil {
cell = UITableViewCell(...)
}
-~~~
-~~~{objective-c}
+```
+```objc
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:...];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:... reuseIdentifier:...];
}
-~~~
+```
`UICollectionView` thankfully does away with this. `-dequeueReusableCellWithReuseIdentifier:forIndexPath:` is guaranteed to return a valid object, by creating a new cell if there are no cells to reuse. Simply register a `UICollectionReusableView` subclass for a particular reuse identifier, and everything will work automatically.
@@ -52,7 +52,7 @@ Because collection views aren't relegated to any particular structure, the conve
Each cell can have multiple supplementary views associated with it--one for each named "kind". As such, headers and footers are just the beginning of what can be done with supplementary views.
-The whole point is that with supplementary views, even the most complex layout can be accomplished without compromising the semantic integrity of cells. `UITableView` hacks are to [`spacer.gif`](http://en.wikipedia.org/wiki/Spacer_GIF) as `UICollectionView` cells are to [semantic HTML](http://en.wikipedia.org/wiki/Semantic_HTML).
+The whole point is that with supplementary views, even the most complex layout can be accomplished without compromising the semantic integrity of cells. `UITableView` hacks are to [`spacer.gif`](https://en.wikipedia.org/wiki/Spacer_GIF) as `UICollectionView` cells are to [semantic HTML](https://en.wikipedia.org/wiki/Semantic_HTML).
### Decoration Views
@@ -88,7 +88,7 @@ What's _extremely_ cool is this method here:
- `-layoutAttributesForElementsInRect:`
-Using this, you could, for example, fade out items as they approach the edge of the screen. Or, since all of the layout attribute properties are automatically animated, you could create a poor-man's [cover flow](http://en.wikipedia.org/wiki/Cover_Flow) layout in just a couple lines of code with the right set of 3D transforms.
+Using this, you could, for example, fade out items as they approach the edge of the screen. Or, since all of the layout attribute properties are automatically animated, you could create a poor-man's [cover flow](https://en.wikipedia.org/wiki/Cover_Flow) layout in just a couple lines of code with the right set of 3D transforms.
In fact, collection views can even swap out layouts wholesale, allowing views to transition seamlessly between different modes--all without changing the underlying data.
diff --git a/2012-10-01-pragma.md b/2012-10-01-pragma.md
index 258d11b1..ed849f26 100644
--- a/2012-10-01-pragma.md
+++ b/2012-10-01-pragma.md
@@ -1,95 +1,206 @@
---
title: "#pragma"
-author: Mattt Thompson
+author: Mattt
category: Objective-C
tags: nshipster
-excerpt: "`#pragma` declarations are a mark of craftsmanship in Objective-C. Although originally purposed for compiling source code across many different compilers, the modern Xcode-savvy programmer uses #pragma declarations to very different ends."
+excerpt: >-
+ `#pragma` declarations are a mark of craftsmanship in Objective-C.
+ Although originally used to make source code compatible across different compilers,
+ Xcode-savvy programmers use `#pragma` to very different ends.
+revisions:
+ 2019-12-13: Updated for Xcode 11
status:
- swift: n/a
+ swift: n/a
---
-`#pragma` declarations are a mark of craftsmanship in Objective-C. Although originally used to make source code compatible between different compilers, the Xcode-savvy coder uses `#pragma` declarations to very different ends.
+`#pragma` declarations are a mark of craftsmanship in Objective-C.
+Although originally used to make source code compatible across different compilers,
+Xcode-savvy programmers use `#pragma` declarations to very different ends.
-In this modern context, `#pragma` skirts the line between comment and code. As a preprocessor directive, `#pragma` evaluates at compile-time. But unlike other macros, such as `#ifdef...#endif`, the way `#pragma` is used will not change the runtime behavior of your application. Instead, `#pragma` declarations are used by Xcode to accomplish two primary tasks: organizing code and inhibiting compiler warnings.
+Whereas other preprocessor directives
+allow you to define behavior when code is executed,
+`#pragma` is unique in that it gives you control
+_at the time code is being written_ ---
+specifically,
+by organizing code under named headings
+and inhibiting warnings.
-> In addition to the `#pragma` syntax, both [GCC](http://gcc.gnu.org/onlinedocs/cpp/Pragmas.html) and [Clang](http://clang.llvm.org/docs/UsersManual.html#controlling-diagnostics-via-pragmas) have added the C99 `_Pragma` operator.
+As we'll see in this week's article,
+good developer habits start with `#pragma mark`.
## Organizing Your Code
-Code organization is a matter of hygiene. How you structure your code is a reflection on you and your work. A lack of convention and internal consistency indicates either carelessness or incompetence--and worse, makes a project difficult to maintain and collaborate on.
-
-Good habits start with `#pragma mark`. Like so:
-
-~~~{objective-c}
+Code organization is a matter of hygiene.
+How you structure your code is a reflection of you and your work.
+A lack of convention and internal consistency indicates
+either carelessness or incompetence ---
+and worse,
+it makes a project more challenging to maintain over time.
+
+We here at NSHipster believe that
+code should be clean enough to eat off of.
+That's why we use `#pragma mark` to divide code into logical sections.
+
+If your class overrides any inherited methods,
+organize them under common headings according to their superclass.
+This has the added benefit of
+describing the responsibilities of each ancestor in the class hierarchy.
+An `NSInputStream` subclass, for instance,
+might have a group marked `NSInputStream`,
+followed by `NSStream`,
+and then finally `NSObject`.
+
+If your class adopts any protocols,
+it makes sense to group each of their respective methods
+using a `#pragma mark` header with the name of that protocol
+_(bonus points for following the same order as the original declarations)_.
+
+Finding it difficult to make sense of your app's MVC architecture?
+_("Massive View Controller", that is.)_
+`#pragma` marks allow you to divide-and-conquer!
+With a little practice,
+you'll be able to identify and organize around common concerns with ease.
+Some headings that tend to come up regularly include
+initializers,
+helper methods,
+`@dynamic` properties,
+Interface Builder outlets or actions,
+and handlers for notification or
+Key-Value Observing (KVO) selectors.
+
+Putting all of these techniques together,
+the `@implementation` for
+a `ViewController` class that inherits from `UITableViewController`
+might organize Interface Builder actions together at the top,
+followed by overridden view controller life-cycle methods,
+and then methods for each adopted protocol,
+each under their respective heading.
+
+```objc
@implementation ViewController
-- (id)init {
- ...
-}
+- (instancetype)init { <#...#> }
-#pragma mark - UIViewController
+#pragma mark - IBAction
-- (void)viewDidLoad {
- ...
-}
+- (IBAction)confirm:(id)sender { <#...#> }
+- (IBAction)cancel:(id)sender { <#...#> }
-#pragma mark - IBAction
+#pragma mark - UIViewController
-- (IBAction)cancel:(id)sender {
- ...
-}
+- (void)viewDidLoad { <#...#> }
+- (void)viewDidAppear:(BOOL)animated { <#...#> }
#pragma mark - UITableViewDataSource
-- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
- ...
-}
+- (NSInteger)tableView:(UITableView *)tableView
+ numberOfRowsInSection:(NSInteger)section { <#...#> }
#pragma mark - UITableViewDelegate
-- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
- ...
-}
-~~~
+- (void)tableView:(UITableView *)tableView
+didSelectRowAtIndexPath:(NSIndexPath *)indexPath { <#...#> }
-Use `#pragma mark` in your `@implementation` to divide code into logical sections. Not only do these sections make it easier to read through the code itself, but it also adds visual cues to the Xcode source navigator (`#pragma mark` declarations starting with a dash (`-`) are preceded with a horizontal divider).
+@end
+```
-
+Not only do these sections make for easier reading in the code itself,
+but they also create visual cues to
+the Xcode source navigator and minimap.
-If your class conforms to any `@protocols`, start by grouping all of the methods within each protocol together, and adding a `#pragma mark` header with the name of that protocol. Another good convention is to group subclassed methods according to their respective superclass. For example, an `NSInputStream` subclass should have a group marked `NSInputStream`, followed by a group marked `NSStream`. Things like `IBAction` outlets, or methods corresponding to target / action, notification, or KVO selectors probably deserve their own sections as well.
+{% asset pragma-xcode-sections.png alt="#pragma headings in the Xcode source navigator"%}
-Your code should be clean enough to eat off of. So take the time to leave your `.m` files better than how you found them.
+{% info %}
-## Inhibiting Warnings
-
-`#pragma mark` is pretty mainstream. On the other hand, `#pragma` declarations to inhibit warnings from the compiler & static analyzer--now that's pretty fresh.
-
-You know what's even more annoying than poorly-formatted code? Code that generates warnings. Especially 3rd-party code. There are few things as irksome as that one vendor library that takes forever to compile, and finishes with 200+ warnings. Even shipping code with 1 warning is in poor form.
+`#pragma mark` declarations beginning with a dash (`-`)
+are preceded with a horizontal divider in Xcode.
-> Pro tip: Try setting the `-Weverything` flag and checking the "Treat Warnings as Errors" box your build settings. This turns on Hard Mode in Xcode.
+{% endinfo %}
-But sometimes there's no avoiding compiler warnings. Deprecation notices and retain-cycle false positives are two common situations where this might happen. In those rare cases where you are _absolutely_ certain that a particular compiler or static analyzer warning should be inhibited, `#pragma` comes to the rescue:
+## Inhibiting Warnings
-~~~{objective-c}
-// completionBlock is manually nilled out in AFURLConnectionOperation to break the retain cycle.
+Do you know what's even more annoying than poorly-formatted code?
+Code that generates warnings ---
+especially 3rd-party code.
+Is there anything more irksome than a vendor SDK that generates dozens of warnings
+each time you hit ⌘R?
+Heck,
+even a single warning is one too many for many developers.
+
+Warnings are almost always warranted,
+but sometimes they're unavoidable.
+In those rare circumstances where you are _absolutely_ certain that
+a particular warning from the compiler or static analyzer is errant,
+`#pragma` comes to the rescue.
+
+Let's say you've deprecated a class:
+
+```objc
+@interface DeprecatedClass __attribute__ ((deprecated))
+<#...#>
+@end
+```
+
+Now,
+deprecation is something to be celebrated.
+It's a way to responsibly communicate future intent to API consumers
+in a way that provides enough time for them to
+migrate to an alternative solution.
+But you might not feel so appreciated if you have
+`CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS` enabled in your project.
+Rather than being praised for your consideration,
+you'd be admonished for implementing a deprecated class.
+
+One way to silence the compiler would be to
+disable the warnings by setting `-Wno-deprecated-implementations`.
+However,
+doing this for the entire project would be too coarse an adjustment,
+and doing this for this file only would be too much work.
+A better option would be to use `#pragma`
+to inhibit the unhelpful warning
+around the problematic code.
+
+Using `#pragma clang diagnostic push/pop`,
+you can tell the compiler to ignore certain warnings
+for a particular section of code:
+
+```objc
#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Warc-retain-cycles"
- self.completionBlock = ^ {
- ...
- };
+#pragma clang diagnostic ignored "-Wdeprecated-implementations"
+@implementation DeprecatedClass
+<#...#>
+@end
#pragma clang diagnostic pop
-~~~
+```
-This code sample [from AFNetworking](https://github.com/AFNetworking/AFNetworking/blob/master/AFNetworking/AFHTTPRequestOperation.m#L247) (contributed by [Peter Steinberger](https://github.com/steipete)) is an example of an otherwise unavoidable warning from the static analyzer. Clang notices a strong reference to `self` within the block, and warns about a possible [retain cycle](http://www.quora.com/What-is-a-retain-cycle). However, the `super` implementation of `setCompletionBlock` takes care of this by `nil`-ing out the strong reference after the completion block is finished.
+Before you start copy-pasting this across your project
+in a rush to zero out your warnings,
+remember that,
+in the overwhelming majority of cases,
+Clang is going to be right.
+Fixing an analyzer warning is _strongly_ preferred to ignoring it,
+so use `#pragma clang diagnostic ignored` as a method of last resort.
-Fortunately, Clang provides a convenient way to get around all of this. Using `#pragma clang diagnostic push/pop`, you can tell the compiler to ignore certain warnings, _only_ for a particular section of code (the original diagnostic settings are restored with the final `pop`).
+{% warning %}
-> You can read more about the LLVM's use of `#pragma` in the [Clang Compiler User's Manual](http://clang.llvm.org/docs/UsersManual.html#diagnostics_pragmas).
+Finding app development to be _too_ easy for you?
+Enable all diagnostics with the
+[`-Weverything`](https://clang.llvm.org/docs/UsersManual.html#diagnostics-enable-everything) flag
+and enable "Treat Warnings as Errors".
+This turns on "Hard Mode" in Xcode.
-Just don't use this as a way to sweep legitimate warnings under the rug--that will only come back to bite you later.
+{% endwarning %}
---
-So there you go: two ways you can markedly improve your code using `#pragma` declarations.
-
-Like the thrift store 8-track player you turned into that lamp in the foyer, `#pragma` remains a curious vestige of the past: Once the secret language of compilers, now re-purposed to better-communicate intent to other programmers. How delightfully vintage!
+Though it skirts the line between comment and code,
+`#pragma` remains a vital tool in the modern app developer's toolbox.
+Whether it's for grouping methods or suppressing spurious warnings,
+judicious use of `#pragma` can go a long way
+to make projects easier to understand and maintain.
+
+Like the thrift store 8-track player you turned into that lamp in the foyer,
+`#pragma` remains a curious vestige of the past:
+Once the secret language of compilers,
+now re-purposed to better-communicate intent to other programmers.
+_How delightfully vintage!_
diff --git a/2012-10-08-at-compiler-directives.md b/2012-10-08-at-compiler-directives.md
deleted file mode 100644
index 20a23e21..00000000
--- a/2012-10-08-at-compiler-directives.md
+++ /dev/null
@@ -1,330 +0,0 @@
----
-title: "@"
-author: Mattt Thompson
-category: Objective-C
-tags: nshipster
-excerpt: "If we were to go code-watching for Objective-C, what would we look for? Square brackets, ridiculously-long method names, and `@`'s. \"at\" sign compiler directives are as central to understanding Objective-C's gestalt as its ancestry and underlying mechanisms. It's the sugary glue that allows Objective-C to be such a powerful, expressive language, and yet still compile all the way down to C."
-status:
- swift: n/a
----
-
-Birdwatchers refer to it as (and I swear I'm not making this up) ["Jizz"](http://en.wikipedia.org/wiki/Jizz_%28birding%29): those indefinable characteristics unique to a particular kind of thing.
-
-This term can be appropriated to describe how seasoned individuals might distinguish [Rust](http://www.rust-lang.org) from [Go](http://golang.org), or [Ruby](http://www.ruby-lang.org) from [Elixir](http://elixir-lang.org) at a glance.
-
-Some just stick out like sore thumbs:
-
-Perl, with all of its short variable names with special characters, reads like [Q\*bert swearing](http://imgur.com/WyG2D).
-
-Lisp, whose profusion of parentheses is best captured by [that old joke](http://discuss.fogcreek.com/joelonsoftware3/default.asp?cmd=show&ixPost=94232&ixReplies=38) about the Russians in the 1980's proving that they had stolen the source code of some SDI missile interceptor code by showing the last page:
-
-~~~ lisp
- )))
- ) )
- ))) ) ))
- )))))
- )))
- ))
- )))) ))
- )))) ))
- )))
-)
-~~~
-
-So if we were to go code-watching for the elusive Objective-C species, what would we look for? That's right:
-
-- Square brackets
-- Ridiculously-long method names
-- `@`'s
-
-`@`, or "at" sign compiler directives, are as central to understanding Objective-C's gestalt as its ancestry and underlying mechanisms. It's the sugary glue that allows Objective-C to be such a powerful, expressive language, and yet still compile all the way down to C.
-
-Its uses are varied and disparate, to the point that the only accurate way to describe what `@` means by itself is "shorthand for something to do with Objective-C". They cover a broad range in usefulness and obscurity, from staples like `@interface` and `@implementation` to ones you could go your whole career without running into, like `@defs` and `@compatibility_alias`.
-
-But to anyone aspiring to be an NSHipster, intimate familiarity with `@` directives is tantamount to a music lover's ability to enumerate the entire Beatles catalog in chronological order (and most importantly, having unreasonably strong opinions about each of them).
-
-## Interface & Implementation
-
-`@interface` and `@implementation` are the first things you learn about when you start Objective-C:
-
-- `@interface`...`@end`
-- `@implementation`...`@end`
-
-What you don't learn about until later on, are categories and class extensions.
-
-Categories allow you to extend the behavior of existing classes by adding new class or instance methods. As a convention, categories are defined in their own `.{h,m}` files, like so:
-
-#### MyObject+CategoryName.h
-
-~~~{objective-c}
-@interface MyObject (CategoryName)
- - (void)foo;
- - (BOOL)barWithBaz:(NSInteger)baz;
-@end
-~~~
-
-#### MyObject+CategoryName.m
-
-~~~{objective-c}
-@implementation MyObject (CategoryName)
- - (void)foo {
- // ...
- }
-
- - (BOOL)barWithBaz:(NSInteger)baz {
- return YES;
- }
-@end
-~~~
-
-Categories are particularly useful for convenience methods on standard framework classes (just don't go overboard with your utility functions).
-
-> Pro Tip: Rather than littering your code with random, arbitrary color values, create an `NSColor` / `UIColor` color palette category that defines class methods like `+appNameDarkGrayColor`. You can then add a semantic layer on top of that by creating method aliases like `+appNameTextColor`, which returns `+appNameDarkGrayColor`.
-
-Extensions look like categories, but omit the category name. These are typically declared before an `@implementation` to specify a private interface, and even override properties declared in the interface:
-
-~~~{objective-c}
-@interface MyObject ()
-@property (readwrite, nonatomic, strong) NSString *name;
-- (void)doSomething;
-@end
-
-@implementation MyObject
-@synthesize name = _name;
-
-// ...
-
-@end
-~~~
-
-### Properties
-
-Property directives are likewise concepts learned early on:
-
-- `@property`
-- `@synthesize`
-- `@dynamic`
-
-One interesting note with properties is that as of Xcode 4.4, it is no longer necessary to explicitly synthesize properties. Properties declared in an `@interface` are automatically synthesized (with leading underscore ivar name, i.e. `@synthesize propertyName = _propertyName`) in the implementation.
-
-### Forward Class Declarations
-
-Occasionally, `@interface` declarations will reference an external class in a property or as a parameter type. Rather than adding `#import` statements for each class, it's good practice to use forward class declarations in the header, and import them in the implementation.
-
-- `@class`
-
-Shorter compile times, less chance of cyclical references; you should definitely get in the habit of doing this if you aren't already.
-
-### Instance Variable Visibility
-
-It's a matter of general convention that classes provide state and mutating interfaces through properties and methods, rather than directly exposing ivars.
-
-Although ARC makes working with ivars much safer by taking care of memory management, the aforementioned automatic property synthesis removes the one place where ivars would otherwise be declared.
-
-Nonetheless, in cases where ivars _are_ directly manipulated, there are the following visibility directives:
-
-- `@public`: instance variable can be read and written to directly, using the notation `person->age = 32"`
-- `@package`: instance variable is public, except outside of the framework in which it is specified (64-bit architectures only)
-- `@protected`: instance variable is only accessible to its class and derived classes
-- `@private`: instance variable is only accessible to its class
-
-~~~{objective-c}
-@interface Person : NSObject {
- @public
- NSString *name;
- int age;
-
- @private
- int salary;
-}
-~~~
-
-## Protocols
-
-There's a distinct point early in an Objective-C programmer's evolution, when she realizes that she can define her own protocols.
-
-The beauty of protocols is that they allow programmers to design contracts that can be adopted outside of a class hierarchy. It's the egalitarian mantra at the heart of the American Dream: that it doesn't matter who you are, or where you come from: anyone can achieve anything if they work hard enough.
-
-...or at least that's idea, right?
-
-- `@protocol`...`@end`: Defines a set of methods to be implemented by any class conforming to the protocol, as if they were added to the interface of that class.
-
-Architectural stability and expressiveness without the burden of coupling--protocols are awesome.
-
-### Requirement Options
-
-You can further tailor a protocol by specifying methods as required or optional. Optional methods are stubbed in the interface, so as to be auto-completed in Xcode, but do not generate a warning if the method is not implemented. Protocol methods are required by default.
-
-The syntax for `@required` and `@optional` follows that of the visibility macros:
-
-~~~{objective-c}
-@protocol CustomControlDelegate
- - (void)control:(CustomControl *)control didSucceedWithResult:(id)result;
-@optional
- - (void)control:(CustomControl *)control didFailWithError:(NSError *)error;
-@end
-~~~
-
-## Exception Handling
-
-Objective-C communicates unexpected state primarily through `NSError`. Whereas other languages would use exception handling for this, Objective-C relegates exceptions to truly exceptional behavior, including programmer error.
-
-`@` directives are used for the traditional convention of `try/catch/finally` blocks:
-
-~~~{objective-c}
-@try{
- // attempt to execute the following statements
- [self getValue:&value error:&error];
-
- // if an exception is raised, or explicitly thrown...
- if (error) {
- @throw exception;
- }
-} @catch(NSException *e) {
- // ...handle the exception here
-} @finally {
- // always execute this at the end of either the @try or @catch block
- [self cleanup];
-}
-~~~
-
-## Literals
-
-Literals are shorthand notation for specifying fixed values. Literals are more
--or-less directly correlated with programmer happiness. By this measure, Objective-C has long been a language of programmer misery.
-
-### Object Literals
-
-Until recently, Objective-C only had literals for `NSString`. But with the release of the [Apple LLVM 4.0 compiler](http://clang.llvm.org/docs/ObjectiveCLiterals.html), literals for `NSNumber`, `NSArray` and `NSDictionary` were added, with much rejoicing.
-
-- `@""`: Returns an `NSString` object initialized with the Unicode content inside the quotation marks.
-- `@42`, `@3.14`, `@YES`, `@'Z'`: Returns an `NSNumber` object initialized with pertinent class constructor, such that `@42` → `[NSNumber numberWithInteger:42]`, or `@YES` → `[NSNumber numberWithBool:YES]`. Supports the use of suffixes to further specify type, like `@42U` → `[NSNumber numberWithUnsignedInt:42U]`.
-- `@[]`: Returns an `NSArray` object initialized with the comma-delimited list of objects as its contents. It uses +arrayWithObjects:count: class constructor method, which is a more precise alternative to the more familiar `+arrayWithObjects:`. For example, `@[@"A", @NO, @2.718]` → `id objects[] = {@"A", @NO, @2.718}; [NSArray arrayWithObjects:objects count:3]`.
-- `@{}`: Returns an `NSDictionary` object initialized with the specified key-value pairs as its contents, in the format: `@{@"someKey" : @"theValue"}`.
-- `@()`: Dynamically evaluates the boxed expression and returns the appropriate object literal based on its value (i.e. `NSString` for `const char*`, `NSNumber` for `int`, etc.). This is also the designated way to use number literals with `enum` values.
-
-### Objective-C Literals
-
-Selectors and protocols can be passed as method parameters. `@selector()` and `@protocol()` serve as pseudo-literal directives that return a pointer to a particular selector (`SEL`) or protocol (`Protocol *`).
-
-- `@selector()`: Returns an `SEL` pointer to a selector with the specified name. Used in methods like `-performSelector:withObject:`.
-- `@protocol()`: Returns a `Protocol *` pointer to the protocol with the specified name. Used in methods like `-conformsToProtocol:`.
-
-### C Literals
-
-Literals can also work the other way around, transforming Objective-C objects into C values. These directives in particular allow us to peek underneath the Objective-C veil, to begin to understand what's really going on.
-
-Did you know that all Objective-C classes and objects are just glorified `struct`s? Or that the entire identity of an object hinges on a single `isa` field in that `struct`?
-
-For most of us, at least most of the time, coming into this knowledge is but an academic exercise. But for anyone venturing into low-level optimizations, this is simply the jumping-off point.
-
-- `@encode()`: Returns the [type encoding](http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html) of a type. This type value can be used as the first argument encode in `NSCoder -encodeValueOfObjCType:at:`.
-- `@defs()`: Returns the layout of an Objective-C class. For example, to declare a struct with the same fields as an `NSObject`, you would simply do:
-
-~~~{objective-c}
-struct {
- @defs(NSObject)
-}
-~~~
-
-> Ed. As pointed out by readers [@secboffin](http://twitter.com/secboffin) & [@ameaijou](http://twitter.com/ameaijou), `@defs` is unavailable in the modern Objective-C runtime.
-
-## Optimizations
-
-There are some `@` compiler directives specifically purposed for providing shortcuts for common optimizations.
-
-- `@autoreleasepool{}`: If your code contains a tight loop that creates lots of temporary objects, you can use the `@autoreleasepool` directive to optimize for these short-lived, locally-scoped objects by being more aggressive about how they're deallocated. `@autoreleasepool` replaces and improves upon the old `NSAutoreleasePool`, which is significantly slower, and unavailable with ARC.
-- `@synchronized(){}`: This directive offers a convenient way to guarantee the safe execution of a particular block within a specified context (usually `self`). Locking in this way is expensive, however, so for classes aiming for a particular level of thread safety, a dedicated `NSLock` property or the use of low-level locking functions like `OSAtomicCompareAndSwap32(3)` are recommended.
-
-## Compatibility
-
-In case all of the previous directives were old hat for you, there's a strong likelihood that you didn't know about this one:
-
-- `@compatibility_alias`: Allows existing classes to be aliased by a different name.
-
-For example [PSTCollectionView](https://github.com/steipete/PSTCollectionView) uses `@compatibility_alias` to significantly improve the experience of using the backwards-compatible, drop-in replacement for [UICollectionView](http://nshipster.com/uicollectionview/):
-
-~~~{objective-c}
-// Allows code to just use UICollectionView as if it would be available on iOS SDK 5.
-// http://developer.apple. com/legacy/mac/library/#documentation/DeveloperTools/gcc-3. 3/gcc/compatibility_005falias.html
-#if __IPHONE_OS_VERSION_MAX_ALLOWED < 60000
-@compatibility_alias UICollectionViewController PSTCollectionViewController;
-@compatibility_alias UICollectionView PSTCollectionView;
-@compatibility_alias UICollectionReusableView PSTCollectionReusableView;
-@compatibility_alias UICollectionViewCell PSTCollectionViewCell;
-@compatibility_alias UICollectionViewLayout PSTCollectionViewLayout;
-@compatibility_alias UICollectionViewFlowLayout PSTCollectionViewFlowLayout;
-@compatibility_alias UICollectionViewLayoutAttributes PSTCollectionViewLayoutAttributes;
-@protocol UICollectionViewDataSource @end
-@protocol UICollectionViewDelegate @end
-#endif
-~~~
-
-Using this clever combination of macros, a developer can develop with `UICollectionView` by including `PSTCollectionView`--without worrying about the deployment target of the final project. As a drop-in replacement, the same code works more-or-less identically on iOS 6 as it does on iOS 4.3.
-
----
-
-So to review:
-
-**Interfaces & Implementation**
-
-- `@interface`...`@end`
-- `@implementation`...`@end`
-- `@class`
-
-**Instance Variable Visibility**
-
-- `@public`
-- `@package`
-- `@protected`
-- `@private`
-
-**Properties**
-
-- `@property`
-- `@synthesize`
-- `@dynamic`
-
-**Protocols**
-
-- `@protocol`
-- `@required`
-- `@optional`
-
-**Exception Handling**
-
-- `@try`
-- `@catch`
-- `@finally`
-- `@throw`
-
-**Object Literals**
-
-- `@""`
-- `@42`, `@3.14`, `@YES`, `@'Z'`
-- `@[]`
-- `@{}`
-- `@()`
-
-**Objective-C Literals**
-
-- `@selector()`
-- `@protocol()`
-
-**C Literals**
-
-- `@encode()`
-- `@defs()`
-
-**Optimizations**
-
-- `@autoreleasepool{}`
-- `@synchronized{}`
-
-**Compatibility**
-
-- `@compatibility_alias`
-
-Thus concludes this exhaustive rundown of the many faces of `@`. It's a versatile, power-packed character, that embodies the underlying design and mechanisms of the language.
-
-> This should be a complete list, but there's always a chance that some new or long-forgotten ones slipped between the cracks. If you know of any `@` directives that were left out, be sure to let [@NSHipster](https://twitter.com/nshipster) know.
diff --git a/2012-10-15-addressbookui.md b/2012-10-15-addressbookui.md
index a8cd9c88..8b1528b1 100644
--- a/2012-10-15-addressbookui.md
+++ b/2012-10-15-addressbookui.md
@@ -1,6 +1,6 @@
---
title: AddressBookUI
-author: Mattt Thompson
+author: Mattt
category: Cocoa
excerpt: "Address Book UI is an iOS framework for displaying, selecting, editing, and creating contacts in a user's Address Book. Similar to the Message UI framework, Address Book UI contains a number of controllers that can be presented modally, to provide common system functionality in a uniform interface."
status:
@@ -28,17 +28,17 @@ The first argument for the function is a dictionary containing the address compo
> `kABPersonAddressCountryCodeKey` is an especially important attribute, as it determines which locale used to format the address string. If you are unsure of the country code, or one isn't provided with your particular data set, `NSLocale` may be able to help you out:
-~~~{swift}
+```swift
let countryCode: String = NSLocale(localeIdentifier: "en_US").objectForKey(NSLocaleCountryCode) as String
-~~~
+```
-~~~{objective-c}
+```objc
[mutableAddressComponents setValue:[[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"] objectForKey:NSLocaleCountryCode] forKey:(__bridge NSString *)kABPersonAddressCountryCodeKey];
-~~~
+```
The second argument is a boolean flag, `addCountryName`. When `YES`, the name of the country corresponding to the specified country code will be automatically appended to the address. This should only used when the country code is known.
-~~~{swift}
+```swift
let addressComponents = [
kABPersonAddressStreetKey: "70 NW Couch Street",
kABPersonAddressCityKey: "Portland",
@@ -48,33 +48,33 @@ let addressComponents = [
]
ABCreateStringWithAddressDictionary(addressComponents, true)
-~~~
+```
-~~~
+```
70 NW Couch Street
Portland OR 97209
United States
-~~~
+```
-Nowhere else in all of the other frameworks is this functionality provided. It's not part of [`NSLocale`](http://nshipster.com/nslocale/), or even Map Kit or Core Location. For all of the care and attention to detail that Apple puts into localization, it's surprising that such an important task is relegated to the corners of an obscure, somewhat-unrelated framework.
+Nowhere else in all of the other frameworks is this functionality provided. It's not part of [`NSLocale`](https://nshipster.com/nslocale/), or even Map Kit or Core Location. For all of the care and attention to detail that Apple puts into localization, it's surprising that such an important task is relegated to the corners of an obscure, somewhat-unrelated framework.
> Unfortunately, Address Book UI is not available in OS X, and it would appear that there's no equivalent function provided on this platform.
For you see, address formats vary greatly across different regions. For example, addresses in the United States take the form:
-~~~
+```
Street Address
City State ZIP
Country
-~~~
+```
Whereas addresses in Japan follow a different convention:
-~~~
+```
Postal Code
Prefecture Municipality
Street Address
Country
-~~~
+```
-This is at least as jarring a difference in localization as [swapping periods for commas the radix point](http://en.wikipedia.org/wiki/Decimal_mark#Hindu.E2.80.93Arabic_numeral_system), so make sure to use this function anytime you're displaying an address from its components.
+This is at least as jarring a difference in localization as [swapping periods for commas the radix point](https://en.wikipedia.org/wiki/Decimal_mark#Hindu.E2.80.93Arabic_numeral_system), so make sure to use this function anytime you're displaying an address from its components.
diff --git a/2012-10-22-nslinguistictagger.md b/2012-10-22-nslinguistictagger.md
index 6d2f69fe..21365421 100644
--- a/2012-10-22-nslinguistictagger.md
+++ b/2012-10-22-nslinguistictagger.md
@@ -1,6 +1,6 @@
---
title: NSLinguisticTagger
-author: Mattt Thompson
+author: Mattt
category: Cocoa
tags: nshipster
excerpt: "NSLinguisticTagger is a veritable Swiss Army Knife of linguistic functionality, with the ability to tokenize natural language strings into words, determine their part-of-speech & stem, extract names of people, places, & organizations, and tell you the languages & respective writing system used in the string."
@@ -9,7 +9,7 @@ status:
reviewed: September 8, 2015
---
-`NSLinguisticTagger` is a veritable Swiss Army Knife of linguistic functionality, with the ability to [tokenize](http://en.wikipedia.org/wiki/Tokenization) natural language strings into words, determine their part-of-speech & [stem](http://en.wikipedia.org/wiki/Word_stem), extract names of people, places, & organizations, and tell you the languages & respective [writing system](http://en.wikipedia.org/wiki/Writing_system) used in the string.
+`NSLinguisticTagger` is a veritable Swiss Army Knife of linguistic functionality, with the ability to [tokenize](https://en.wikipedia.org/wiki/Tokenization) natural language strings into words, determine their part-of-speech & [stem](https://en.wikipedia.org/wiki/Word_stem), extract names of people, places, & organizations, and tell you the languages & respective [writing system](https://en.wikipedia.org/wiki/Writing_system) used in the string.
For most of us, this is far more power than we know what to do with. But perhaps this is just for lack sufficient opportunity to try. After all, almost every application deals with natural language in one way or another--perhaps `NSLinguisticTagger` could add a new level of polish, or enable brand new features entirely.
@@ -23,7 +23,7 @@ Consider a typical question we might ask Siri:
Computers are a long ways off from "understanding" this question literally, but with a few simple tricks, we can do a reasonable job understanding the _intention_ of the question:
-~~~{swift}
+```swift
let question = "What is the weather in San Francisco?"
let options: NSLinguisticTaggerOptions = [.OmitWhitespace, .OmitPunctuation, .JoinNames]
let schemes = NSLinguisticTagger.availableTagSchemesForLanguage("en")
@@ -33,8 +33,8 @@ tagger.enumerateTagsInRange(NSMakeRange(0, (question as NSString).length), schem
let token = (question as NSString).substringWithRange(tokenRange)
println("\(token): \(tag)")
}
-~~~
-~~~{objective-c}
+```
+```objc
NSString *question = @"What is the weather in San Francisco?";
NSLinguisticTaggerOptions options = NSLinguisticTaggerOmitWhitespace | NSLinguisticTaggerOmitPunctuation | NSLinguisticTaggerJoinNames;
NSLinguisticTagger *tagger = [[NSLinguisticTagger alloc] initWithTagSchemes: [NSLinguisticTagger availableTagSchemesForLanguage:@"en"] options:options];
@@ -43,7 +43,7 @@ tagger.string = question;
NSString *token = [question substringWithRange:tokenRange];
NSLog(@"%@: %@", token, tag);
}];
-~~~
+```
This code would print the following:
@@ -56,7 +56,7 @@ This code would print the following:
If we filter on nouns, verbs, and place name, we get `[is, weather, San Francisco]`.
-Just based on this alone, or perhaps in conjunction with something like the [Latent Semantic Mapping](http://developer.apple.com/library/mac/#documentation/LatentSemanticMapping/Reference/LatentSemanticMapping_header_reference/Reference/reference.html) framework, we can conclude that a reasonable course of action would be to make an API request to determine the current weather conditions in San Francisco.
+Just based on this alone, or perhaps in conjunction with something like the [Latent Semantic Mapping](https://developer.apple.com/library/mac/#documentation/LatentSemanticMapping/Reference/LatentSemanticMapping_header_reference/Reference/reference.html) framework, we can conclude that a reasonable course of action would be to make an API request to determine the current weather conditions in San Francisco.
## Tagging Schemes
@@ -72,54 +72,54 @@ Here's a list of the various token types associated with each scheme (`NSLinguis
-
NSLinguisticTagSchemeTokenType
-
NSLinguisticTagSchemeLexicalClass
-
NSLinguisticTagSchemeNameType
+
NSLinguisticTagSchemeTokenType
+
NSLinguisticTagSchemeLexicalClass
+
NSLinguisticTagSchemeNameType
-
NSLinguisticTagWord
-
NSLinguisticTagPunctuation
-
NSLinguisticTagWhitespace
-
NSLinguisticTagOther
+
NSLinguisticTagWord
+
NSLinguisticTagPunctuation
+
NSLinguisticTagWhitespace
+
NSLinguisticTagOther
-
NSLinguisticTagNoun
-
NSLinguisticTagVerb
-
NSLinguisticTagAdjective
-
NSLinguisticTagAdverb
-
NSLinguisticTagPronoun
-
NSLinguisticTagDeterminer
-
NSLinguisticTagParticle
-
NSLinguisticTagPreposition
-
NSLinguisticTagNumber
-
NSLinguisticTagConjunction
-
NSLinguisticTagInterjection
-
NSLinguisticTagClassifier
-
NSLinguisticTagIdiom
-
NSLinguisticTagOtherWord
-
NSLinguisticTagSentenceTerminator
-
NSLinguisticTagOpenQuote
-
NSLinguisticTagCloseQuote
-
NSLinguisticTagOpenParenthesis
-
NSLinguisticTagCloseParenthesis
-
NSLinguisticTagWordJoiner
-
NSLinguisticTagDash
-
NSLinguisticTagOtherPunctuation
-
NSLinguisticTagParagraphBreak
-
NSLinguisticTagOtherWhitespace
+
NSLinguisticTagNoun
+
NSLinguisticTagVerb
+
NSLinguisticTagAdjective
+
NSLinguisticTagAdverb
+
NSLinguisticTagPronoun
+
NSLinguisticTagDeterminer
+
NSLinguisticTagParticle
+
NSLinguisticTagPreposition
+
NSLinguisticTagNumber
+
NSLinguisticTagConjunction
+
NSLinguisticTagInterjection
+
NSLinguisticTagClassifier
+
NSLinguisticTagIdiom
+
NSLinguisticTagOtherWord
+
NSLinguisticTagSentenceTerminator
+
NSLinguisticTagOpenQuote
+
NSLinguisticTagCloseQuote
+
NSLinguisticTagOpenParenthesis
+
NSLinguisticTagCloseParenthesis
+
NSLinguisticTagWordJoiner
+
NSLinguisticTagDash
+
NSLinguisticTagOtherPunctuation
+
NSLinguisticTagParagraphBreak
+
NSLinguisticTagOtherWhitespace
-
NSLinguisticTagPersonalName
-
NSLinguisticTagPlaceName
-
NSLinguisticTagOrganizationName
+
NSLinguisticTagPersonalName
+
NSLinguisticTagPlaceName
+
NSLinguisticTagOrganizationName
diff --git a/2012-10-29-uilocalizedindexedcollation.md b/2012-10-29-uilocalizedindexedcollation.md
index e0274042..542f4307 100644
--- a/2012-10-29-uilocalizedindexedcollation.md
+++ b/2012-10-29-uilocalizedindexedcollation.md
@@ -1,6 +1,6 @@
---
title: UILocalizedIndexedCollation
-author: Mattt Thompson
+author: Mattt
category: Cocoa
tags: nshipster
excerpt: "UITableView starts to become unwieldy once it gets to a few hundred rows. If users are reduced to frantically scratching at the screen like a cat playing Fruit Ninja in order to get at what they want... you may want to rethink your UI approach."
@@ -9,17 +9,17 @@ status:
reviewed: September 8, 2015
---
-UITableView starts to become unwieldy once it gets to a few hundred rows. If users are reduced to frantically scratching at the screen like a [cat playing Fruit Ninja](http://www.youtube.com/watch?v=CdEBgZ5Y46U) in order to get at what they want... you may want to rethink your UI approach.
+UITableView starts to become unwieldy once it gets to a few hundred rows. If users are reduced to frantically scratching at the screen like a [cat playing Fruit Ninja](https://www.youtube.com/watch?v=CdEBgZ5Y46U) in order to get at what they want... you may want to rethink your UI approach.
So, what are your options?
-Well, you could organize your data into a hierarchy, which could dramatically reduce the number of rows displayed on each screen in fashion, based on its [branching factor](http://en.wikipedia.org/wiki/Branching_factor).
+Well, you could organize your data into a hierarchy, which could dramatically reduce the number of rows displayed on each screen in fashion, based on its [branching factor](https://en.wikipedia.org/wiki/Branching_factor).
You could also add a `UISearchBar` to the top of your table view, allowing the user to filter on keywords to get exactly what they're looking for (or--perhaps more importantly--determine that what they seek doesn't exist in the first place).
There is also a third approach, which is generally under-utilized in iOS applications: **section index titles**. These are the vertically flowing letters found along the right side of table views in your Address Book contacts list or Music library:
-
+
As the user scrolls their finger down the list, the table view jumps to the corresponding section. Even the most tiresome table view is rendered significantly more usable as a result.
@@ -43,13 +43,13 @@ To give you a better idea of how section index titles vary between locales:
> In order to see these for yourself, you'll need to explicitly add the desired locales to your Project Localizations list.
-| Locale | Section Index Titles |
-|------------|----------------------|
-| en_US | `A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, #` |
-| ja_JP | `A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, あ, か, さ, た, な, は, ま, や, ら, わ, #` |
-| sv_SE | `A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, Å, Ä, Ö, #` |
-| ko_KO | `A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, ㄱ, ㄴ, ㄷ, ㄹ, ㅁ, ㅂ, ㅅ, ㅇ, ㅈ, ㅊ, ㅋ, ㅌ, ㅍ, ㅎ, #` |
-| AR_sa | A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, آ, ب, ت, ث, ج, ح, خ, د, ذ, ر, ز, س, ش, ص, ض, ط, ظ, ع, غ, ف, ق, ك, ل, م, ن, ه, و, ي, # |
+| Locale | Section Index Titles |
+| ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| en_US | `A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, #` |
+| ja_JP | `A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, あ, か, さ, た, な, は, ま, や, ら, わ, #` |
+| sv_SE | `A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, Å, Ä, Ö, #` |
+| ko_KO | `A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, ㄱ, ㄴ, ㄷ, ㄹ, ㅁ, ㅂ, ㅅ, ㅇ, ㅈ, ㅊ, ㅋ, ㅌ, ㅍ, ㅎ, #` |
+| AR_sa | A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, آ, ب, ت, ث, ج, ح, خ, د, ذ, ر, ز, س, ش, ص, ض, ط, ظ, ع, غ, ف, ق, ك, ل, م, ن, ه, و, ي, # |
Aren't you glad you don't have to do this yourself?
@@ -61,19 +61,19 @@ Finally, the table view should implement `-tableView:sectionForSectionIndexTitle
All told, here's what a typical table view data source implementation looks like:
-~~~{swift}
+```swift
class ObjectTableViewController: UITableViewController {
- let collation = UILocalizedIndexedCollation.currentCollation()
+ let collation = UILocalizedIndexedCollation.current()
var sections: [[AnyObject]] = []
var objects: [AnyObject] = [] {
didSet {
- let selector: Selector = "localizedTitle"
- sections = Array(count: collation.sectionTitles.count, repeatedValue: [])
+ let selector = #selector(getter: UIApplicationShortcutItem.localizedTitle)
+ sections = Array(repeating: [], count: collation.sectionTitles.count)
- let sortedObjects = collation.sortedArrayFromArray(objects, collationStringSelector: selector)
+ let sortedObjects = collation.sortedArray(from: objects, collationStringSelector: selector)
for object in sortedObjects {
- let sectionNumber = collation.sectionForObject(object, collationStringSelector: selector)
- sections[sectionNumber].append(object)
+ let sectionNumber = collation.section(for: object, collationStringSelector: selector)
+ sections[sectionNumber].append(object as AnyObject)
}
self.tableView.reloadData()
@@ -82,21 +82,21 @@ class ObjectTableViewController: UITableViewController {
// MARK: UITableViewDelegate
- override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String {
+ override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return collation.sectionTitles[section]
}
- override func sectionIndexTitlesForTableView(tableView: UITableView) -> [String] {
+ override func sectionIndexTitles(for tableView: UITableView) -> [String]? {
return collation.sectionIndexTitles
}
- override func tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int {
- return collation.sectionForSectionIndexTitleAtIndex(index)
+ override func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
+ return collation.section(forSectionIndexTitle: index)
}
}
-~~~
+```
-~~~{objective-c}
+```objc
- (void)setObjects:(NSArray *)objects {
SEL selector = @selector(localizedTitle);
NSInteger index, sectionTitlesCount = [[[UILocalizedIndexedCollation currentCollation] sectionTitles] count];
@@ -137,7 +137,7 @@ sectionForSectionIndexTitle:(NSString *)title
{
return [[UILocalizedIndexedCollation currentCollation] sectionForSectionIndexTitleAtIndex:index];
}
-~~~
+```
## UITableViewIndexSearch
@@ -152,4 +152,3 @@ So remember, NSHipsters one and all: if you see an excessively long table view,
...which is to say, refactor your content with some combination of hierarchies, a search bar, and section indexes. And when implementing section index titles, take advantage of `UILocalizedIndexedCollation`.
Together, we can put an end to scroll view-induced repetitive stress injuries, and spend more time enjoying the finer things in life, like watching videos of pets playing with iPads.
-
diff --git a/2012-11-05-nsurlprotocol.md b/2012-11-05-nsurlprotocol.md
index f823c90d..b40513c4 100644
--- a/2012-11-05-nsurlprotocol.md
+++ b/2012-11-05-nsurlprotocol.md
@@ -1,101 +1,19 @@
---
title: NSURLProtocol
-author: Mattt Thompson
+author: Mattt
category: Cocoa
excerpt: "Foundation’s URL Loading System is something that every iOS developer would do well to buddy up with. And of all of networking classes and protocols of Foundation, NSURLProtocol is perhaps the most obscure and powerful."
status:
- swift: n/a
+ swift: n/a
---
iOS is all about networking--whether it's reading or writing state to and from the server, offloading computation to a distributed system, or loading remote images, audio, and video from the cloud.
-Because of this, Foundation's [URL Loading System](https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html#//apple_ref/doc/uid/10000165i) is something that every iOS developer would do well to buddy up with.
-
-When given the choice, applications should adopt the highest-level framework possible for what needs to be done. So, if that task is communicating over `http://`, `https://` or `ftp://`, then `NSURLConnection` and friends are a clear choice. Apple's networking classes cover the essentials for modern Objective-C application development, from URL and cache management to authentication & cookie storage:
-
-
- The URL Loading System
-
-
-
-
URL Loading
-
-
-
-
-
NSURLConnection
-
-
-
NSURLRequest
-
NSMutableURLRequest
-
-
-
NSURLResponse
-
NSHTTPURLResponse
-
-
-
-
-
Cache Management
-
-
-
-
-
NSURLCache
-
-
-
NSCacheURLRequest
-
-
-
NSCachedURLResponse
-
-
-
-
-
Authentication & Credentials
-
-
-
-
-
NSURLCredential
-
-
-
NSURLCredentialStorage
-
-
-
NSURLAuthenticationChallenge
-
-
-
NSURLProtectionSpace
-
-
-
-
-
Cookie Storage
-
-
-
-
-
NSHTTPCookie
-
-
-
NSHTTPCookieStorage
-
-
-
-
-
Protocol Support
-
-
-
-
-
NSURLProtocol
-
-
-
-
-
-Although there's a lot to the URL Loading System, it's designed in a way that hides the underlying complexity, with hooks to provide configuration when needed. Any request going through `NSURLConnection` is intercepted by other parts of the system along the way, allowing for things like cached responses being transparently loaded from disk when available.
+Because of this, Foundation's [URL Loading System](https://developer.apple.com/documentation/foundation/url_loading_system) is something that every iOS developer would do well to buddy up with.
+
+When given the choice, applications should adopt the highest-level framework possible for what needs to be done. So, if that task is communicating over `http://`, `https://` or `ftp://`, then `NSURLSession` and friends are a clear choice. Apple's networking classes cover the essentials for modern app development, from URL and cache management to authentication & cookie storage.
+
+Although there's a lot to the URL Loading System, it's designed in a way that hides the underlying complexity, with hooks to provide configuration when needed. Any request going through `NSURLSession` is intercepted by other parts of the system along the way, allowing for things like cached responses being transparently loaded from disk when available.
Which brings us to this week's topic: `NSURLProtocol`.
@@ -112,7 +30,7 @@ If you aren't already `mindblown.gif`, here are some examples of what this can b
- Creating a proxy server for a local data transformation service with a URL request interface
- Deliberately sending malformed & illegal response data to test the robustness of the application
- Filtering sensitive information from requests or responses
-- Implementing an `NSURLConnection`-compatible interface to an existing protocol.
+- Implementing an `NSURLSession`-compatible interface to an existing protocol.
Again, it's important to reiterate that the whole point of `NSURLProtocol` is that you can change everything about the loading behavior of your application without doing anything differently with how your application communicates to the network.
@@ -146,18 +64,18 @@ This is especially important for subclasses created to interact with protocols t
The most important methods in your subclass are `-startLoading` and `-stopLoading`. What goes into either of these methods is entirely dependent on what your subclass is trying to accomplish, but there is one commonality: communicating with the protocol client.
-Each instance of a `NSURLProtocol` subclass has a `client` property, which is the object that is communicating with the URL Loading system. It's not `NSURLConnection`, but the object does conform to a protocol that should look familiar to anyone who has implemented `NSURLConnectionDelegate`
+Each instance of a `NSURLProtocol` subclass has a `client` property, which is the object that is communicating with the URL Loading system. It's not `NSURLSession`, but the object does conform to a protocol that should look familiar to anyone who has implemented a session delegate.
#### ``
-* `-URLProtocol:cachedResponseIsValid:`
-* `-URLProtocol:didCancelAuthenticationChallenge:`
-* `-URLProtocol:didFailWithError:`
-* `-URLProtocol:didLoadData:`
-* `-URLProtocol:didReceiveAuthenticationChallenge:`
-* `-URLProtocol:didReceiveResponse:cacheStoragePolicy:`
-* `-URLProtocol:wasRedirectedToRequest:redirectResponse:`
-* `-URLProtocolDidFinishLoading:`
+- `-URLProtocol:cachedResponseIsValid:`
+- `-URLProtocol:didCancelAuthenticationChallenge:`
+- `-URLProtocol:didFailWithError:`
+- `-URLProtocol:didLoadData:`
+- `-URLProtocol:didReceiveAuthenticationChallenge:`
+- `-URLProtocol:didReceiveResponse:cacheStoragePolicy:`
+- `-URLProtocol:wasRedirectedToRequest:redirectResponse:`
+- `-URLProtocolDidFinishLoading:`
In your implementation of `-startLoading` and `-stopLoading`, you will need to send each delegate method to your `client` when appropriate. For something simple, this may mean sending several in rapid succession, but it's important nonetheless.
@@ -170,7 +88,3 @@ When a request is loaded, each registered protocol is asked "hey, can you handle
---
Like the URL Loading System that contains it, `NSURLProtocol` is incredibly powerful, and can be used in exceedingly clever ways. As a relatively obscure class, we've only just started to mine its potential for how we can use it to make our code cleaner, faster, and more robust.
-
-So go forth and hack! I can't wait to see what y'all come up with!
-
-
diff --git a/2012-11-12-nsvaluetransformer.md b/2012-11-12-nsvaluetransformer.md
deleted file mode 100644
index 4975906b..00000000
--- a/2012-11-12-nsvaluetransformer.md
+++ /dev/null
@@ -1,133 +0,0 @@
----
-title: NSValueTransformer
-author: Mattt Thompson
-category: Cocoa
-tags: nshipster
-excerpt: "Of all the Foundation classes, NSValueTransformer is perhaps the one that fared the worst in the shift from OS X to iOS. But you know what? It's 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."
-status:
- swift: 2.0
- reviewed: September 8, 2015
----
-
-Of all the Foundation classes, `NSValueTransformer` is perhaps the one that fared the worst in the shift from OS X to iOS.
-
-Why? Well, there are two reasons:
-
-The first and most obvious reason is that `NSValueTransformer` was mainly used in AppKit with Cocoa bindings. Here, they could automatically transform values from one property to another without the need of intermediary glue code, like for negating a boolean, or checking whether a value was `nil`. 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 `NSValueTransformer` or `NSInvocation`. So even if iOS were to get bindings tomorrow, it's uncertain as to whether `NSValueTransformer` would play as significant a role this time around.
-
-But you know what? `NSValueTransformer` is 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.
-
----
-
-`NSValueTransformer` is an abstract class that transforms one value into another. A transformation specifies what kinds of input values can be handled, and can even supports reversible transformations, where applicable.
-
-A typical implementation would look something like this:
-
-~~~{swift}
-class ClassNameTransformer: NSValueTransformer {
-
- override class func transformedValueClass() -> AnyClass {
- return NSString.self
- }
-
- override class func allowsReverseTransformation() -> Bool {
- return false
- }
-
- override func transformedValue(value: AnyObject?) -> AnyObject? {
- guard let type = value as? AnyClass else { return nil }
- return NSStringFromClass(type)
- }
-}
-~~~
-
-~~~{objective-c}
-@interface ClassNameTransformer: NSValueTransformer {}
-@end
-
-#pragma mark -
-
-@implementation ClassNameTransformer
-+ (Class)transformedValueClass {
- return [NSString class];
-}
-
-+ (BOOL)allowsReverseTransformation {
- return NO;
-}
-
-- (id)transformedValue:(id)value {
- return (value == nil) ? nil : NSStringFromClass([value class]);
-}
-@end
-~~~
-
-`NSValueTransformer` 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 the _instance_ to act as a singleton (with a particular name):
-
-~~~{swift}
-let ClassNameTransformerName = "ClassNameTransformer"
-
-// Set the value transformer
-NSValueTransformer.setValueTransformer(ClassNameTransformer(), forName: ClassNameTransformerName)
-
-// Get the value transformer
-let valueTransformer = NSValueTransformer(forName: ClassNameTransformerName)
-~~~
-
-~~~{objective-c}
-NSString * const ClassNameTransformerName = @"ClassNameTransformer";
-
-// Set the value transformer
-[NSValueTransformer setValueTransformer:[[ClassNameTransformer alloc] init] forName:ClassNameTransformerName];
-
-// Get the value transformer
-NSValueTransformer *valueTransformer = [NSValueTransformer valueTransformerForName:ClassNameTransformerName];
-~~~
-
-Typically, the singleton instance would be registered in the `+initialize` method of the value transformer subclass, so it could be used without further setup.
-
-Now, at this point, you probably realize `NSValueTransformer`'s fatal flaw: it's a pain in the ass 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](https://github.com/mattt/TransformerKit/blob/master/TransformerKit/NSValueTransformer%2BTransformerKit.m#L36) can't fix. Behold:
-
-~~~{swift}
-let TKCapitalizedStringTransformerName = "TKCapitalizedStringTransformerName"
-
-NSValueTransformer.registerValueTransformerWithName(TKCapitalizedStringTransformerName,
- transformedValueClass:NSString.self) { obj in
- guard let str = obj as? String else { return nil }
- return str.capitalizedString
-}
-~~~
-~~~{objective-c}
-NSString * const TKCapitalizedStringTransformerName = @"TKCapitalizedStringTransformerName";
-
-[NSValueTransformer registerValueTransformerWithName:TKCapitalizedStringTransformerName
- transformedValueClass:[NSString class]
-returningTransformedValueWithBlock:^id(id value) {
- return [value capitalizedString];
-}];
-~~~
-
-Not to break the 4th wall or anything, but in the middle of writing this article, I was compelled to see what could be done to improve the experience of `NSValueTransformer`. What I came up with was [TransformerKit](https://github.com/mattt/TransformerKit).
-
-The entire library is based on some objc runtime hackery in an `NSValueTransformer` category. Also included with this category are a number of convenient examples, like string case transformers (i.e. `CamelCase`, `llamaCase`, `snake_case`, and `train-case`).
-
-Now with its sexy new getup, we start to form a better understanding of where this could be useful:
-
-- `NSValueTransformers` are the ideal 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.
-- Unlike blocks, `NSValueTransformer` encapsulates reversible transformations. Let's say you were wanted to map keys from a REST API representation into a Model object; 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.
-- Another advantage over blocks is that `NSValueTransformer` subclasses can expose new properties that could be used to configure behavior in a particular way. Access to `ivars` also make it easier to cleanly memoize results, or do any necessary book-keeping along the way.
-- Lest we forget, `NSValueTransformer` can be used with Core Data, as a way 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, for example, is an excellent strategy for information that isn't well-modeled as its own entity. Just don't serialize images to a database this way--that's generally a Bad Idea™.
-
-And the list goes on.
-
----
-
-`NSValueTransformer`, far from a vestige of AppKit, remains Foundation's purest connection to that fundamental concept of computation: input goes in, output comes out.
-
-Although it hasn't aged very well on its own, a little modernization restores `NSValueTransformer` to that highest esteem of NSHipsterdom: the solution that we didn't know we needed, but was there all along.
diff --git a/2012-11-12-valuetransformer.md b/2012-11-12-valuetransformer.md
new file mode 100644
index 00000000..af0cd83c
--- /dev/null
+++ b/2012-11-12-valuetransformer.md
@@ -0,0 +1,235 @@
+---
+title: ValueTransformer
+author: Mattt
+category: Cocoa
+tags: nshipster
+excerpt: >-
+ `ValueTransformer` is perhaps the one that fared the worst
+ in the shift from macOS to iOS.
+ But you know what? It's 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.
+revisions:
+ "2012-11-12": Original publication
+ "2018-10-17": Updated for Swift 4.2
+status:
+ swift: 4.2
+ reviewed: October 17, 2018
+---
+
+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 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:
+
+```swift
+class ClassNameTransformer: ValueTransformer {
+ override class func transformedValueClass() -> AnyClass {
+ return NSString.self
+ }
+
+ override class func allowsReverseTransformation() -> Bool {
+ return false
+ }
+
+ override func transformedValue(_ value: Any?) -> Any? {
+ guard let type = value as? AnyClass else { return nil }
+ return NSStringFromClass(type)
+ }
+}
+```
+
+```objc
+@interface ClassNameTransformer: NSValueTransformer {}
+@end
+
+#pragma mark -
+
+@implementation ClassNameTransformer
++ (Class)transformedValueClass {
+ return [NSString class];
+}
+
++ (BOOL)allowsReverseTransformation {
+ return NO;
+}
+
+- (id)transformedValue:(id)value {
+ return (value == nil) ? nil : NSStringFromClass([value class]);
+}
+@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:
+
+```swift
+extension ClassNameTransformer {
+ static let name = NSValueTransformerName(rawValue: "ClassNameTransformer")
+}
+
+// Set the value transformer
+ValueTransformer.setValueTransformer(ClassNameTransformer(),
+ forName: ClassNameTransformer.name)
+
+// Get the value transformer
+let valueTransformer = ValueTransformer(forName: ClassNameTransformer.name)
+```
+
+```objc
+NSValueTransformerName const ClassNameTransformerName = @"ClassNameTransformer";
+
+// Set the value transformer
+[NSValueTransformer setValueTransformer:[[ClassNameTransformer alloc] init] forName:ClassNameTransformerName];
+
+// Get the value transformer
+NSValueTransformer *valueTransformer = [NSValueTransformer valueTransformerForName: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](https://github.com/mattt/TransformerKit/blob/master/TransformerKit/NSValueTransformer%2BTransformerKit.m#L36) can't fix.
+Behold:
+
+```swift
+let TKCapitalizedStringTransformerName =
+ NSValueTransformerName(rawValue: "TKCapitalizedStringTransformerName")
+
+ValueTransformer.registerValueTransformerWithName(TKCapitalizedStringTransformerName,
+ transformedValueClass:NSString.self) { object in
+ guard let string = object as? String else { return nil }
+ return string.capitalized
+}
+```
+
+```objc
+NSValueTransformerName const TKCapitalizedStringTransformerName = @"TKCapitalizedStringTransformerName";
+
+[NSValueTransformer registerValueTransformerWithName:TKCapitalizedStringTransformerName
+ transformedValueClass:[NSString class]
+returningTransformedValueWithBlock:^id(id value) {
+ return [value capitalizedString];
+}];
+```
+
+{% info %}
+Not to break the 4th wall or anything,
+but in the process of writing this article,
+I was compelled to see how much I could improve
+the experience of using `ValueTransformer`.
+What I came up with was
+[TransformerKit](https://github.com/mattt/TransformerKit).
+
+At the core of the library is some Obj-C runtime hackery
+in an `ValueTransformer` category.
+Also included are some helpful examples,
+like string case transformers
+(i.e. `CamelCase`, `llamaCase`, `snake_case`, and `train-case`).
+{% endinfo %}
+
+---
+
+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.
diff --git a/2012-11-19-ns_enum-ns_options.md b/2012-11-19-ns_enum-ns_options.md
index 0bfda2d7..8577f88f 100644
--- a/2012-11-19-ns_enum-ns_options.md
+++ b/2012-11-19-ns_enum-ns_options.md
@@ -1,6 +1,6 @@
---
title: "NS_ENUM & NS_OPTIONS"
-author: Mattt Thompson
+author: Mattt
category: Cocoa
tags: nshipster, popular
excerpt: "A skilled Objective-C developer is able to gracefully switch between Objective and Procedural paradigms, and use each to their own advantage."
@@ -27,47 +27,39 @@ And on that note, this week's topic has to do with two simple-but-handy macros:
Introduced in Foundation with iOS 6 / OS X Mountain Lion, the `NS_ENUM` and `NS_OPTIONS` macros are the new, preferred way to declare `enum` types.
-> If you'd like to use either macro when targeting a previous version of iOS or OS X, you can simply inline like so:
-
-~~~{objective-c}
-#ifndef NS_ENUM
-#define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
-#endif
-~~~
-
`enum`, or enumerated value types, are the C way to define constants for fixed values, like days of the week, or available styles of table view cells. In an `enum` declaration, constants without explicit values will automatically be assigned values sequentially, starting from `0`.
There are several legal ways that `enum`s can be defined. What's confusing is that there are subtle functional differences between each approach, and without knowing any better, someone is just as likely to use them interchangeably.
For instance:
-~~~{objective-c}
+```objc
enum {
UITableViewCellStyleDefault,
UITableViewCellStyleValue1,
UITableViewCellStyleValue2,
UITableViewCellStyleSubtitle
};
-~~~
+```
...declares integer values, but no type.
Whereas:
-~~~{objective-c}
+```objc
typedef enum {
UITableViewCellStyleDefault,
UITableViewCellStyleValue1,
UITableViewCellStyleValue2,
UITableViewCellStyleSubtitle
} UITableViewCellStyle;
-~~~
+```
...defines the `UITableViewCellStyle` type, suitable for specifying the type of method parameters.
However, Apple had previously defined all of their `enum` types as:
-~~~{objective-c}
+```objc
typedef enum {
UITableViewCellStyleDefault,
UITableViewCellStyleValue1,
@@ -76,7 +68,7 @@ typedef enum {
};
typedef NSInteger UITableViewCellStyle;
-~~~
+```
...which ensures a fixed size for `UITableViewCellStyle`, but does nothing to hint the relation between the aforementioned `enum` and the new type to the compiler.
@@ -86,14 +78,14 @@ Thankfully, Apple has decided on "One Macro To Rule Them All" with `NS_ENUM`.
Now, `UITableViewCellStyle` is declared with:
-~~~{objective-c}
+```objc
typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
UITableViewCellStyleDefault,
UITableViewCellStyleValue1,
UITableViewCellStyleValue2,
UITableViewCellStyleSubtitle
};
-~~~
+```
The first argument for `NS_ENUM` is the type used to store the new type. In a 64-bit environment, `UITableViewCellStyle` will be 8 bytes long--same as `NSInteger`. Make sure that the specified size can fit all of the defined values, or else an error will be generated. The second argument is the name of the new type (as you probably guessed). Inside the block, the values are defined as usual.
@@ -101,7 +93,7 @@ This approach combines the best of all of the aforementioned approaches, and eve
## `NS_OPTIONS`
-`enum` can also be used to define a [bitmask][1]. Using a convenient property of binary math, a single integer value can encode a combination of values all at once using the bitwise `OR` (`|`), and decoded with bitwise `AND` (`&`). Each subsequent value, rather than automatically being incremented by 1 from 0, are manually given a value with a bit offset: `1 << 0`, `1 << 1`, `1 << 2`, and so on. If you imagine the binary representation of a number, like `10110` for 22, each bit can be thought to represent a single boolean. In UIKit, for example, `UIViewAutoresizing` is a bitmask that can represent any combination of flexible top, bottom, left, and right margins, or width and height.
+`enum` can also be used to define a [bitmask][1]. Using a convenient property of binary math, a single integer value can encode a combination of values all at once using the bitwise `OR` (`|`), and decoded with bitwise `AND` (`&`). Each subsequent value, rather than automatically being incremented by 1 from 0, are manually given a value with a bit offset: `1 << 0`, `1 << 1`, `1 << 2`, and so on. If you imagine the binary representation of a number, like `10110` for 22, each bit can be though to represent a single boolean. In UIKit, for example, `UIViewAutoresizing` is a bitmask that can represent any combination of flexible top, bottom, left, and right margins, or width and height.
Rather than `NS_ENUM`, bitmasks should now use the `NS_OPTIONS` macro.
@@ -111,4 +103,4 @@ The syntax is exactly the same as `NS_ENUM`, but this macro alerts the compiler
`NS_ENUM` and `NS_OPTIONS` are handy additions to the Objective-C development experience, and reaffirm the healthy dialectic between its objective and procedural nature. Keep this in mind as you move forward in your own journey to understand the logical tensions that underpin everything around us.
-[1]: http://en.wikipedia.org/wiki/Mask_(computing)
+[1]: https://en.wikipedia.org/wiki/Mask_(computing)
diff --git a/2012-11-26-nsorderedset.md b/2012-11-26-nsorderedset.md
index f44f4f21..2d9556d0 100644
--- a/2012-11-26-nsorderedset.md
+++ b/2012-11-26-nsorderedset.md
@@ -1,17 +1,17 @@
---
title: NSOrderedSet
-author: Mattt Thompson
+author: Mattt
category: Cocoa
tags: nshipster
excerpt: "Why isn't NSOrderedSet a subclass of NSSet? The answer may surprise you."
status:
- swift: 2.0
- reviewed: September 15, 2015
+ swift: 2.0
+ reviewed: September 15, 2015
---
Here's a question: why isn't `NSOrderedSet` a subclass of `NSSet`?
-It seems perfectly logical, after all, for `NSOrderedSet`--a class that enforces the same uniqueness constraint of `NSSet`--to be a _subclass_ of `NSSet`. It has the same methods as `NSSet`, with the addition of some `NSArray`-style methods like `objectAtIndex:`. By all accounts, it would seem to perfectly satisfy the requirements of the [Liskov substitution principle](http://en.wikipedia.org/wiki/Liskov_substitution_principle), that:
+It seems perfectly logical, after all, for `NSOrderedSet`--a class that enforces the same uniqueness constraint of `NSSet`--to be a _subclass_ of `NSSet`. It has the same methods as `NSSet`, with the addition of some `NSArray`-style methods like `objectAtIndex:`. By all accounts, it would seem to perfectly satisfy the requirements of the [Liskov substitution principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle), that:
> If `S` is a subtype of `T`, then objects of type `T` in a program may be replaced with objects of type `S` without altering any of the desirable properties of that program.
@@ -19,7 +19,7 @@ So why is `NSOrderedSet` a subclass of `NSObject` and not `NSSet` or even `NSArr
_Mutable / Immutable Class Clusters_
-[Class Clusters](http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/CocoaObjects.html%23//apple_ref/doc/uid/TP40002974-CH4-SW34) are a design pattern at the heart of the Foundation framework; the essence of Objective-C's simplicity in everyday use.
+[Class Clusters](https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/CocoaObjects.html%23//apple_ref/doc/uid/TP40002974-CH4-SW34) are a design pattern at the heart of the Foundation framework; the essence of Objective-C's simplicity in everyday use.
But class clusters offer simplicity at the expense of extensibility, which becomes especially tricky when it comes to mutable / immutable class pairs like `NSSet` / `NSMutableSet`.
@@ -27,25 +27,25 @@ As expertly demonstrated by [Tom Dalling](http://tomdalling.com) in [this Stack
To start, let's look at how `-mutableCopy` is supposed to work in a class cluster:
-~~~{swift}
+```swift
let immutable = NSSet()
let mutable = immutable.mutableCopy() as! NSMutableSet
mutable.isKindOfClass(NSSet.self) // true
mutable.isKindOfClass(NSMutableSet.self) // true
-~~~
+```
-~~~{objective-c}
+```objc
NSSet* immutable = [NSSet set];
NSMutableSet* mutable = [immutable mutableCopy];
[mutable isKindOfClass:[NSSet class]]; // YES
[mutable isKindOfClass:[NSMutableSet class]]; // YES
-~~~
+```
Now let's suppose that `NSOrderedSet` was indeed a subclass of `NSSet`:
-~~~{swift}
+```swift
// class NSOrderedSet: NSSet {...}
let immutable = NSOrderedSet()
@@ -53,9 +53,9 @@ let mutable = immutable.mutableCopy() as! NSMutableOrderedSet
mutable.isKindOfClass(NSSet.self) // true
mutable.isKindOfClass(NSMutableSet.self) // false (!)
-~~~
+```
-~~~{objective-c}
+```objc
// @interface NSOrderedSet : NSSet
NSOrderedSet* immutable = [NSOrderedSet orderedSet];
@@ -63,13 +63,13 @@ NSMutableOrderedSet* mutable = [immutable mutableCopy];
[mutable isKindOfClass:[NSSet class]]; // YES
[mutable isKindOfClass:[NSMutableSet class]]; // NO (!)
-~~~
+```
-
+{% asset nsorderedset-case-1.svg %}"
That's no good... since `NSMutableOrderedSet` couldn't be used as a method parameter of type `NSMutableSet`. So what happens if we make `NSMutableOrderedSet` a subclass of `NSMutableSet` as well?
-~~~{swift}
+```swift
// class NSOrderedSet: NSSet {...}
// class NSMutableOrderedSet: NSMutableSet {...}
@@ -79,9 +79,9 @@ let mutable = immutable.mutableCopy() as! NSMutableOrderedSet
mutable.isKindOfClass(NSSet.self) // true
mutable.isKindOfClass(NSMutableSet.self) // true
mutable.isKindOfClass(NSOrderedSet.self) // false (!)
-~~~
+```
-~~~{objective-c}
+```objc
// @interface NSOrderedSet : NSSet
// @interface NSMutableOrderedSet : NSMutableSet
@@ -91,19 +91,19 @@ NSMutableOrderedSet* mutable = [immutable mutableCopy];
[mutable isKindOfClass:[NSSet class]]; // YES
[mutable isKindOfClass:[NSMutableSet class]]; // YES
[mutable isKindOfClass:[NSOrderedSet class]]; // NO (!)
-~~~
+```
-
+{% asset nsorderedset-case-2.svg %}
This is perhaps even worse, as now `NSMutableOrderedSet` couldn't be used as a method parameter expecting an `NSOrderedSet`.
No matter how we approach it, we can't stack a mutable / immutable class pair on top of another existing mutable / immutable class pair. It just won't work in Objective-C.
-Rather than subject ourselves to the perils of [multiple inheritance](http://en.wikipedia.org/wiki/Multiple_inheritance), we could use Protocols to get us out of this pickle (as it does every other time the spectre of multiple inheritance is raised). Indeed, Foundation's collection classes _could_ become more aspect-oriented by adding protocols:
+Rather than subject ourselves to the perils of [multiple inheritance](https://en.wikipedia.org/wiki/Multiple_inheritance), we could use Protocols to get us out of this pickle (as it does every other time the spectre of multiple inheritance is raised). Indeed, Foundation's collection classes _could_ become more aspect-oriented by adding protocols:
-* `NSArray : NSObject `
-* `NSSet : NSObject `
-* `NSOrderedSet : NSObject `
+- `NSArray : NSObject `
+- `NSSet : NSObject `
+- `NSOrderedSet : NSObject `
However, to reap any benefit from this arrangement, all of the existing APIs would have to be restructured to have parameters accept `id ` instead of `NSArray`. But the transition would be painful, and would likely open up a whole can of edge cases... which would mean that it would never be fully adopted... which would mean that there's less incentive to adopt this approach when defining your own APIs... which are less fun to write because there's now two incompatible ways to do something instead of one... which...
@@ -111,7 +111,7 @@ However, to reap any benefit from this arrangement, all of the existing APIs wou
---
-`NSOrderedSet` was introduced in iOS 5 & OS X Lion. The only APIs changed to add support for `NSOrderedSet`, though, were part of [Core Data](http://developer.apple.com/library/mac/#releasenotes/DataManagement/RN-CoreData/_index.html).
+`NSOrderedSet` was introduced in iOS 5 & OS X Lion. The only APIs changed to add support for `NSOrderedSet`, though, were part of [Core Data](https://developer.apple.com/library/mac/#releasenotes/DataManagement/RN-CoreData/_index.html).
This was fantastic news for anyone using Core Data at the time, as it solved one of the long-standing annoyances of not having a way to arbitrarily order relationship collections. Previously, you'd have to add a `position` attribute, which would be re-calculated every time a collection was modified. There wasn't a built-in way to validate that your collection positions were unique or that the sequence didn't have any gaps.
@@ -123,6 +123,6 @@ Although it is perfectly suited to that one particular use case in Core Data, `N
---
-So, as a general rule: **`NSOrderedSet` is useful for intermediary and internal representations, but you probably shouldn't introduce it as a method parameter unless it's particularly well-suited to the semantics of the data model.**
+So, as a general rule: **`NSOrderedSet` is useful for intermediary and internal representations, but you probably shouldn't introduce it as a method parameters unless it's particularly well-suited to the semantics of the data model.**
If nothing else, `NSOrderedSet` illuminates some of the fascinating implications of Foundation's use of the class cluster design pattern. In doing so, it allows us better understand the trade-off between simplicity and extensibility as we make these choices in our own application designs.
diff --git a/2012-12-03-kvc-collection-operators.md b/2012-12-03-kvc-collection-operators.md
index a0418dc8..30e01486 100644
--- a/2012-12-03-kvc-collection-operators.md
+++ b/2012-12-03-kvc-collection-operators.md
@@ -1,6 +1,6 @@
---
title: KVC Collection Operators
-author: Mattt Thompson
+author: Mattt
category: Cocoa
tags: nshipster
excerpt: "Rubyists laugh at Objective-C’s bloated syntax. Although we lost a few pounds over the summer with our sleek new object literals, those Red-headed bullies still taunt us with their map one-liners and their fancy Symbol#to_proc. Fortunately, Key-Value Coding has an ace up its sleeves."
@@ -11,27 +11,27 @@ status:
Rubyists laugh at Objective-C's bloated syntax.
-Although we lost a few pounds over the summer with our [sleek new object literals](http://nshipster.com/at-compiler-directives/), those Red-headed bullies still taunt us with their `map` one-liners and their fancy [`Symbol#to_proc`](http://pragdave.me/blog/2005/11/04/symboltoproc/).
+Although we lost a few pounds over the summer with our [sleek new object literals](https://nshipster.com/at-compiler-directives/), those Red-headed bullies still taunt us with their `map` one-liners and their fancy [`Symbol#to_proc`](http://pragdave.me/blog/2005/11/04/symboltoproc/).
-Really, a lot of how elegant (or clever) a language is comes down to how well it avoids loops. `for`, `while`; even [fast enumeration expressions](http://nshipster.com/enumerators/) are a drag. No matter how you sugar-coat them, loops will be a block of code that does something that is much simpler to describe in natural language.
+Really, a lot of how elegant (or clever) a language is comes down to how well it avoids loops. `for`, `while`; even [fast enumeration expressions](https://nshipster.com/enumerators/) are a drag. No matter how you sugar-coat them, loops will be a block of code that does something that is much simpler to describe in natural language.
"get me the average salary of all of the employees in this array", versus...
-~~~{objective-c}
+```objc
double totalSalary = 0.0;
for (Employee *employee in employees) {
totalSalary += [employee.salary doubleValue];
}
double averageSalary = totalSalary / [employees count];
-~~~
+```
Meh.
Fortunately, [Key-Value Coding](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueCoding/Articles/KeyValueCoding.html) gives us a much more concise--almost Ruby-like--way to do this:
-~~~{objective-c}
+```objc
[employees valueForKeyPath:@"@avg.salary"];
-~~~
+```
[KVC Collection Operators](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueCoding/Articles/CollectionOperators.html) allows actions to be performed on a collection using key path notation in `valueForKeyPath:`. Any time you see `@` in a key path, it denotes a particular aggregate function whose result can be returned or chained, just like any other key path.
@@ -43,13 +43,13 @@ Collection Operators fall into one of three different categories, according to t
The best way to understand how these work is to see them in action. Consider a `Product` class, and a `products` array with the following data:
-~~~{objective-c}
+```objc
@interface Product : NSObject
@property NSString *name;
@property double price;
@property NSDate *launchedOn;
@end
-~~~
+```
> Key-Value Coding automatically boxes and un-boxes scalars into `NSNumber` or `NSValue` as necessary to make everything work.
@@ -95,32 +95,38 @@ The best way to understand how these work is to see them in action. Consider a `
_Example_:
-~~~{objective-c}
+```objc
[products valueForKeyPath:@"@count"]; // 4
[products valueForKeyPath:@"@sum.price"]; // 3526.00
[products valueForKeyPath:@"@avg.price"]; // 881.50
[products valueForKeyPath:@"@max.price"]; // 1699.00
[products valueForKeyPath:@"@min.launchedOn"]; // June 11, 2012
-~~~
+```
-> Pro Tip: To get the aggregate value of an array or set of `NSNumber`s, you can simply pass `self` as the key path after the operator, e.g. `[@[@(1), @(2), @(3)] valueForKeyPath:@"@max.self"]` (/via [@davandermobile](http://twitter.com/davandermobile), citing [Objective Sea](http://objectivesea.tumblr.com/post/34552840247/max-value-nsset-kvc))
+{% info %}
+To get the aggregate value of an array or set of `NSNumber` values,
+you can simply pass `self` as the key path after the operator.
+For example,
+`[@[@(1), @(2), @(3)] valueForKeyPath:@"@max.self"]`
+produces the value `3`.
+{% endinfo %}
### Object Operators
Let's say we have an `inventory` array, representing the current stock of our local Apple store (which is running low on iPad Mini, and doesn't have the new iMac, which hasn't shipped yet):
-~~~{objective-c}
+```objc
NSArray *inventory = @[iPhone5, iPhone5, iPhone5, iPadMini, macBookPro, macBookPro];
-~~~
+```
- `@unionOfObjects` / `@distinctUnionOfObjects`: Returns an array of the objects in the property specified in the key path to the right of the operator. `@distinctUnionOfObjects` removes duplicates, whereas `@unionOfObjects` does not.
_Example_:
-~~~{objective-c}
+```objc
[inventory valueForKeyPath:@"@unionOfObjects.name"]; // "iPhone 5", "iPhone 5", "iPhone 5", "iPad Mini", "MacBook Pro", "MacBook Pro"
[inventory valueForKeyPath:@"@distinctUnionOfObjects.name"]; // "iPhone 5", "iPad Mini", "MacBook Pro"
-~~~
+```
### Array and Set Operators
@@ -133,9 +139,9 @@ This would be useful if we were to, for example, compare the inventory of severa
_Example_:
-~~~{objective-c}
+```objc
[@[appleStoreInventory, verizonStoreInventory] valueForKeyPath:@"@distinctUnionOfArrays.name"]; // "iPhone 5", "iPad Mini", "MacBook Pro"
-~~~
+```
---
@@ -149,19 +155,19 @@ This makes sense to spell out, since that's what most people are thinking about
However, as it turns out, it _is_ actually possible, with a little help from our friend, `objc/runtime`.
-[Guy English](https://twitter.com/gte) has a [pretty amazing post](http://kickingbear.com/blog/archives/9) wherein he [swizzles `valueForKeyPath:`](https://gist.github.com/4196641#file_kb_collection_extensions.m) to parse a custom-defined [DSL](http://en.wikipedia.org/wiki/Domain-specific_language), which extends the existing offerings to interesting effect:
+[Guy English](https://twitter.com/gte) has a [pretty amazing post](http://kickingbear.com/blog/archives/9) wherein he [swizzles `valueForKeyPath:`](https://gist.github.com/4196641#file_kb_collection_extensions.m) to parse a custom-defined [DSL](https://en.wikipedia.org/wiki/Domain-specific_language), which extends the existing offerings to interesting effect:
-~~~{objective-c}
+```objc
NSArray *names = [allEmployees valueForKeyPath: @"[collect].{daysOff<10}.name"];
-~~~
+```
This code would get the names of anyone who has taken fewer than 10 days off (to remind them to take a vacation, no doubt!).
Or, taken to a ridiculous extreme:
-~~~{objective-c}
+```objc
NSArray *albumCovers = [records valueForKeyPath:@"[collect].{artist like 'Bon Iver'}..albumCoverImageData"];
-~~~
+```
Eat your heart out, Ruby. This one-liner filters a record collection for artists whose name matches "Bon Iver", and initializes an `NSImage` from the album cover image data of the matching albums.
diff --git a/2012-12-10-instancetype.md b/2012-12-10-instancetype.md
index cdab91f8..dcc53d49 100644
--- a/2012-12-10-instancetype.md
+++ b/2012-12-10-instancetype.md
@@ -1,6 +1,6 @@
---
title: instancetype
-author: Mattt Thompson
+author: Mattt
category: Objective-C
excerpt: "Objective-C is a rapidly evolving language, in a way that you just don't see in established programming languages. Developments range from the mundane to paradigm-changing, but telling the difference takes practice. Because we're talking about low-level language features, it's difficult to understand what implications they may have higher up with API design."
status:
@@ -11,7 +11,7 @@ Want to know what's coming next in Objective-C? [Keep your ear to the ground](ht
Objective-C is a rapidly evolving language, in a way that you just don't see in established programming languages. ARC, object literals, subscripting, blocks: in the span of just three years, so much of how we program in Objective-C has been changed (for the better).
-All of this innovation is a result of Apple's philosophy of vertical integration. Just as Apple's investment in designing [its own chipsets](http://en.wikipedia.org/wiki/Apple_A4) gave them leverage to compete aggressively with their mobile hardware, so too has their investment in [LLVM](http://llvm.org) allowed their software to keep pace.
+All of this innovation is a result of Apple's philosophy of vertical integration. Just as Apple's investment in designing [its own chipsets](https://en.wikipedia.org/wiki/Apple_A4) gave them leverage to compete aggressively with their mobile hardware, so too has their investment in [LLVM](http://llvm.org) allowed their software to keep pace.
Clang developments range from the mundane to paradigm-changing, but telling the difference takes practice. Because we're talking about low-level language features, it's difficult to understand what implications they may have higher up with API design.
@@ -29,11 +29,11 @@ Class constructor methods, although they similarly return `id`, don't get the sa
You can try this out for yourself:
-~~~{objective-c}
+```objc
[[[NSArray alloc] init] mediaPlaybackAllowsAirPlay]; // ❗ "No visible @interface for `NSArray` declares the selector `mediaPlaybackAllowsAirPlay`"
[[NSArray array] mediaPlaybackAllowsAirPlay]; // (No error)
-~~~
+```
Because `alloc` and `init` follow the naming convention for being a related result type, the correct type check against `NSArray` is performed. However, the equivalent class constructor `array` does not follow that convention, and is interpreted as `id`.
@@ -45,17 +45,17 @@ This is where the compiler steps in to resolve this timeless edge case to the Ob
`instancetype` is a contextual keyword that can be used as a result type to signal that a method returns a related result type. For example:
-~~~{objective-c}
+```objc
@interface Person
+ (instancetype)personWithName:(NSString *)name;
@end
-~~~
+```
> `instancetype`, unlike `id`, can only be used as the result type in a method declaration.
With `instancetype`, the compiler will correctly infer that the result of `+personWithName:` is an instance of a `Person`.
-Look for class constructors in Foundation to start using `instancetype` in the near future. New APIs, such as [UICollectionViewLayoutAttributes](http://developer.apple.com/library/ios/#documentation/uikit/reference/UICollectionViewLayoutAttributes_class/Reference/Reference.html) are using `instancetype` already.
+Look for class constructors in Foundation to start using `instancetype` in the near future. New APIs, such as [UICollectionViewLayoutAttributes](https://developer.apple.com/library/ios/#documentation/uikit/reference/UICollectionViewLayoutAttributes_class/Reference/Reference.html) are using `instancetype` already.
## Further Implications
@@ -63,9 +63,9 @@ Language features are particularly interesting because, again, it's often unclea
While `instancetype` may seem to be a rather mundane, albeit welcome addition to the compiler, it can be used to some rather clever ends.
-[Jonathan Sterling](https://twitter.com/jonsterling) wrote [this quite interesting article](http://www.jonmsterling.com/posts/2012-02-05-typed-collections-with-self-types-in-objective-c.html), detailing how `instancetype` could be used to encode statically-typed collections, without [generics](http://en.wikipedia.org/wiki/Generic_programming):
+[Jonathan Sterling](https://twitter.com/jonsterling) wrote [this quite interesting article](http://www.jonmsterling.com/posts/2012-02-05-typed-collections-with-self-types-in-objective-c.html), detailing how `instancetype` could be used to encode statically-typed collections, without [generics](https://en.wikipedia.org/wiki/Generic_programming):
-~~~{objective-c}
+```objc
NSURL *sites = (id)[NSURL mapCollection];
[sites put:[NSURL URLWithString:@"http://www.jonmsterling.com/"]
at:@"jon"];
@@ -73,7 +73,7 @@ NSURL *sites = (id)[NSURL mapCollection];
at:@"nshipster"];
NSURL *jonsSite = [sites at:@"jon"]; // => http://www.jonmsterling.com/
-~~~
+```
Statically-typed collections would make APIs more expressive--no longer would a developer be unsure about what kinds of objects are allowed in a collection parameter.
@@ -87,4 +87,4 @@ Know it, love it.
And take it as an example of how paying attention to the low-level details can give you insights into powerful new ways to transform Objective-C.
-[1]: http://en.wikipedia.org/wiki/C_Sharp_(programming_language)
+[1]: https://en.wikipedia.org/wiki/C_Sharp_(programming_language)
diff --git a/2012-12-17-cggeometry.md b/2012-12-17-cggeometry.md
deleted file mode 100644
index 72604090..00000000
--- a/2012-12-17-cggeometry.md
+++ /dev/null
@@ -1,334 +0,0 @@
----
-title: CGGeometry
-author: Mattt Thompson
-category: Cocoa
-excerpt: "Unless you were a Math Geek or an Ancient Greek, Geometry was probably not your favorite subject in high school. No, chances are that you were that kid in class who dutifully programmed all of the necessary formulae into your TI-8X calculator. Keeping in the tradition of doing the least amount of math possible, here are some semi-obscure CoreGraphics functions to make your job easier."
-revisions:
- "2015-02-17": Added Swift examples and information about how `Swift + CGRect == awesome`; added section for `CGRectIntersect` and `CGRectUnion`.
-status:
- swift: 2.0
- reviewed: September 8, 2015
----
-
-Unless you were a Math Geek or an Ancient Greek, Geometry was probably not your favorite subject in high school. No, chances are that you were that kid in class who dutifully programmed all of the necessary formulæ into your TI-8X calculator.
-
-So for those of you who spent more time learning TI-BASIC than Euclidean geometry, here's the cheat-sheet for how geometry works in [Quartz 2D][1], the drawing system used in iOS and OS X:
-
-- A `CGPoint` is a struct that represents a point in a two-dimensional coordinate system. For iOS, the origin is at the top-left, so points move right and down as their `x` and `y` values, respectively, increase. OS X, by contrast, is oriented with `(0, 0)` in the bottom left, with `y` moving up as it increases.
-
-- A `CGSize` is a struct that represents the dimensions of `width` and `height`.
-
-- A `CGRect` is a struct with both a `CGPoint` (`origin`) and a `CGSize` (`size`), representing a rectangle drawn from its `origin` point with the `width` and `height` of its `size`.
-
-Because `CGRect` is used to represent the `frame` of every view drawn on screen, a programmer's success in graphical programming is contingent on their ability to effectively manipulate rectangle geometry.
-
-Fortunately for us, Quartz comes with a slew of useful functions to reduce the amount of floating point math we have to do ourselves. As central as view programming is to Cocoa, and as useful as these functions are, however, they remain relatively unknown to most iOS developers.
-
-This will not stand! Let's shine some light on the most useful functions and save y'all some typing!
-
-> In Swift, the CoreGraphics framework augments `CGRect` by adding all this functionality as static properties, instance properties, and both mutating and non-mutating functions where appropriate. For idiomatic Swift code, prefer properties and nonmutating instance methods whenever possible.
-
----
-
-Transformations
----------------
-
-First on our list are the geometric transformations. These functions return a `CGRect`, which is the result of performing a particular set of operations on the passed rectangle.
-
-### `CGRectOffset`
-
-> `offsetBy` / `CGRectOffset`: Returns a rectangle with an origin that is offset from that of the source rectangle.
-
-~~~{swift}
-// methods:
-extension CGRect {
- func offsetBy(dx: CGFloat, dy: CGFloat) -> CGRect
- mutating func offsetInPlace(dx: CGFloat, dy: CGFloat)
-}
-// function:
-func CGRectOffset(rect: CGRect, dx: CGFloat, dy: CGFloat) -> CGRect
-~~~
-~~~{objective-c}
-CGRect CGRectOffset(
- CGRect rect,
- CGFloat dx,
- CGFloat dy
-)
-~~~
-
-Consider using this anytime you're changing the origin of a rectangle. Not only can it save a line of code when changing both the horizontal and vertical position, but more importantly, it represents the translation more semantically than manipulating the origin values individually.
-
-### `CGRectInset`
-
-> `rectByInsetting` / `CGRectInset`: Returns a rectangle that is smaller or larger than the source rectangle, with the same center point.
-
-~~~{swift}
-// methods:
-extension CGRect {
- func insetBy(dx: CGFloat, dy: CGFloat) -> CGRect
- mutating func insetInPlace(dx: CGFloat, dy: CGFloat)
-}
-// function:
-func CGRectInset(rect: CGRect, dx: CGFloat, dy: CGFloat) -> CGRect
-~~~
-~~~{objective-c}
-CGRect CGRectInset(
- CGRect rect,
- CGFloat dx,
- CGFloat dy
-)
-~~~
-
-Want to make a view-within-a-view look good? Give it a nice 10pt padding with `CGRectInset`. Keep in mind that the rectangle will be resized around its center by ± `dx` on its left and right edge (for a total of `2 × dx`), and ± `dy` on its top and bottom edge (for a total of `2 × dy`).
-
-If you're using `CGRectInset` as a convenience function for resizing a rectangle, it is common to chain this with `CGRectOffset` by passing the result of `CGRectInset` as the `rect` argument in `CGRectOffset`.
-
-### `CGRectIntegral`
-
-> `integral` / `CGRectIntegral`: Returns the smallest rectangle that results from converting the source rectangle values to integers.
-
-~~~{swift}
-// methods:
-extension CGRect {
- var integral: CGRect { get }
- mutating func makeIntegralInPlace()
-}
-// function:
-func CGRectIntegral(rect: CGRect) -> CGRect
-~~~
-~~~{objective-c}
-CGRect CGRectIntegral (
- CGRect rect
-)
-~~~
-
-It's important that `CGRect` values all are rounded to the nearest whole point. Fractional values cause the frame to be drawn on a _pixel boundary_. Because pixels are atomic units (cannot be subdivided†) a fractional value will cause the drawing to be averaged over the neighboring pixels, which looks blurry.
-
-`CGRectIntegral` will `floor` each origin value, and `ceil` each size value, which will ensure that your drawing code will crisply align on pixel boundaries.
-
-As a rule of thumb, if you are performing any operations that could result in fractional point values (e.g. division, `CGRectGetMid[X|Y]`, or `CGRectDivide`), use `CGRectIntegral` to normalize rectangles to be set as a view frame.
-
-> † Technically, since the coordinate system operates in terms of points, Retina screens, which have 4 pixels for every point, can draw `± 0.5f` point values on odd pixels without blurriness.
-
-
-Value Helper Functions
-----------------------
-
-These functions provide a shorthand way to calculate interesting dimensional values about a particular `CGRect`.
-
-### `CGRectGet[Min|Mid|Max][X|Y]`
-
-- `CGRectGetMinX`
-- `CGRectGetMinY`
-- `CGRectGetMidX`
-- `CGRectGetMidY`
-- `CGRectGetMaxX`
-- `CGRectGetMaxY`
-
-These six functions return the minimum, middle, or maximum `x` or `y` value for a rectangle, taking the form:
-
-~~~{swift}
-func CGRectGet[Min|Mid|Max][X|Y](rect: CGRect) -> CGPoint
-~~~
-~~~{objective-c}
-CGFloat CGRectGet[Min|Mid|Max][X|Y] (
- CGRect rect
-)
-~~~
-
-These functions will replace code like `frame.origin.x + frame.size.width` with cleaner, more semantically expressive equivalents (especially with the mid and max functions).
-
-### `CGRectGet[Width|Height]`
-
-~~~{swift}
-// Returns the height of a rectangle.
-func CGRectGetHeight(rect: CGRect) -> CGFloat
-
-// Returns the width of a rectangle.
-func CGRectGetWidth(rect: CGRect) -> CGFloat
-~~~
-~~~{objective-c}
-// Returns the height of a rectangle.
-CGFloat CGRectGetHeight (
- CGRect rect
-)
-
-// Returns the width of a rectangle.
-CGFloat CGRectGetWidth (
- CGRect rect
-)
-~~~
-
-Much like the previous functions, `CGRectGetWidth` & `CGRectGetHeight` are often preferable to returning the corresponding member of a `CGRect`'s `size`. While it's not extremely competitive in terms of character savings, remember that semantic clarity trumps brevity every time.
-
-### Swift Additions
-
-~~~{swift}
-extension CGRect {
- var minX: CGFloat { get }
- var minY: CGFloat { get }
- var midX: CGFloat { get }
- var midY: CGFloat { get }
- var maxX: CGFloat { get }
- var maxY: CGFloat { get }
- var width: CGFloat { get }
- var height: CGFloat { get }
-}
-~~~
-
-The `CGRect` Swift extensions for dimensional values make a huge difference in code readability, as each of the value helper functions is mapped to individual computed properties.
-
-~~~{swift}
-// instead of calling the function
-let rightEdge = CGRectMaxX(view.frame)
-
-// simply access the property
-let leftEdge = view.frame.minX
-~~~
-
-
-Identities
-----------
-
-There are three special rectangle values, each of which have unique properties that are important to know about:
-
-### `CGRectZero`, `CGRectNull`, & `CGRectInfinite`
-
-> - `CGRect.zero` / `const CGRect CGRectZero`: A rectangle constant with location (0,0), and width and height of 0. The zero rectangle is equivalent to CGRectMake(0.0f, 0.0f, 0.0f, 0.0f).
-> - `CGRect.null` / `const CGRect CGRectNull`: The null rectangle. This is the rectangle returned when, for example, you intersect two disjoint rectangles. **Note that the null rectangle is not the same as the zero rectangle**.
-> - `CGRect.infinite` / `const CGRect CGRectInfinite`: A rectangle that has infinite extent.
-
-`CGRectZero` is perhaps the most useful of all of the special rectangle values. When initializing subviews, their frames are often initialized to `CGRectZero`, deferring their layout to `-layoutSubviews`.
-
-`CGRectNull` is distinct from `CGRectZero`, despite any implied correspondence to `NULL` == `0`. This value is conceptually similar to `NSNotFound`, in that it represents the absence of an expected value. Be aware of what functions can return `CGRectNull`, and be prepared to handle it accordingly, by testing with `CGRectIsNull`.
-
-`CGRectInfinite` is the most exotic of all, and has some of the most interesting properties. It intersects with all points and rectangles, contains all rectangles, and its union with any rectangle is itself. Use `CGRectIsInfinite` to check to see if a rectangle is infinite.
-
-
-Relationships
--------------
-
-Moving from one rectangle to two, a pair of rectangles can be either intersected or combined to create a new `CGRect`:
-
-### `CGRectIntersection`
-
-> `intersect` / `CGRectIntersection`: Returns the intersection of two rectangles.
-
-~~~{swift}
-// methods:
-extension CGRect {
- func intersect(withRect: CGRect) -> CGRect
- mutating func intersectInPlace(withRect: CGRect)
-}
-// function:
-func CGRectIntersection(rect1: CGRect, rect2: CGRect) -> CGRect
-~~~
-~~~{objective-c}
-CGRect CGRectIntersection (
- CGRect rect1,
- CGRect rect2
-)
-~~~
-
-`CGRectIntersection` is a fast way to find the overlapping region between two views. The intersection of two non-overlapping rectangles is a null rect, but if you need to simply check for intersection or containment, use `intersects` / `CGRectIntersectsRect` or `contains` / `CGRectContainsRect` instead.
-
-
-### `CGRectUnion`
-
-> `union` / `CGRectUnion`: Returns the smallest rectangle that contains the two source rectangles.
-
-~~~{swift}
-// methods:
-extension CGRect {
- func union(rect: CGRect) -> CGRect
- mutating func unionInPlace(rect: CGRect)
-}
-// function:
-func CGRectUnion(rect1: CGRect, rect2: CGRect) -> CGRect
-~~~
-~~~{objective-c}
-CGRect CGRectUnion (
- CGRect rect1,
- CGRect rect2
-)
-~~~
-
-Need a rectangle that can wrap two separate regions in your view? Remember that you can chain together different methods to produce the rectangle you need. Use `CGRectUnion` and negative values with `CGRectInset` to find a padded rectangle around two items:
-
-~~~{swift}
-let combinedRect = imageRect.union(textRect).insetBy(dx: -10, dy: -10)
-~~~
-~~~{objective-c}
-CGRect combinedRect = CGRectInset(CGRectUnion(imageRect, textRect), -10, -10);
-~~~
-
-And Finally...
---------------
-
-Behold, the most obscure, misunderstood, and useful of the `CGGeometry` functions: `CGRectDivide`.
-
-### `CGRectDivide`
-
-> `CGRectDivide`: Divides a source rectangle into two component rectangles.
-
-~~~{swift}
-// method:
-extension CGRect {
- func divide(atDistance: CGFloat, fromEdge: CGRectEdge) -> (slice: CGRect, remainder: CGRect)
-}
-// function:
-CGRectDivide(rect: CGRect,
- slice: UnsafeMutablePointer,
- remainder: UnsafeMutablePointer,
- amount: CGFloat,
- edge: CGRectEdge)
-~~~
-~~~{objective-c}
-void CGRectDivide(
- CGRect rect,
- CGRect *slice,
- CGRect *remainder,
- CGFloat amount,
- CGRectEdge edge
-)
-~~~
-
-`CGRectDivide` divides a rectangle into two components in the following way:
-
-- Take a rectangle and choose an `edge` (left, right, top, or bottom).
-- Measure out an `amount` from that edge.
-- Everything from the `edge` to the measured `amount` is stored in the rectangle referenced in the `slice` argument.
-- The rest of the original rectangle is stored in the `remainder` out argument.
-
-> Don't fret about the `UnsafeMutablePointer` in the Swift version; those pointers act just like `inout` properties in this case. Create your slice and remainder instances up-front, and prefix with an `&` in the call. Or better yet, use the instance method on an existing `CGRect`:
->
-> `let (slice, remainder) = frame.divide(120, fromEdge: .MinXEdge)`
-
-That `edge` argument takes a value from the `CGRectEdge` enum:
-
-~~~{swift}
-enum CGRectEdge {
- case MinXEdge
- case MinYEdge
- case MaxXEdge
- case MaxYEdge
-}
-~~~
-~~~{objective-c}
-enum CGRectEdge {
- CGRectMinXEdge,
- CGRectMinYEdge,
- CGRectMaxXEdge,
- CGRectMaxYEdge
-}
-~~~
-
-`CGRectDivide` is perfect for dividing up available space among several views (call it on subsequent `remainder` amounts to accommodate more than two views). Give it a try next time you're manually laying-out a `UITableViewCell`.
-
----
-
-So what if you didn't pay attention in Geometry class—this is the real world, and in the real world, you have `CGGeometry.h`.
-
-Know it well, and you'll be on your way to discovering great new user interfaces in your apps. Do good enough of a job with that, and you may run into the greatest arithmetic problem of all: adding up all of the money you'll make with your awesome new app. Mathematical!
-
-[1]: https://developer.apple.com/library/mac/#documentation/graphicsimaging/Conceptual/drawingwithquartz2d/Introduction/Introduction.html#//apple_ref/doc/uid/TP30001066
diff --git a/2013-01-01-new-years-2013.md b/2013-01-01-new-years-2013.md
index 2283e156..b3af4cca 100644
--- a/2013-01-01-new-years-2013.md
+++ b/2013-01-01-new-years-2013.md
@@ -1,10 +1,8 @@
---
title: "Reader Submissions - New Year's 2013"
-author: Mattt Thompson
+author: Mattt
category: Reader Submissions
excerpt: "In celebration of the forthcoming `year++`, I thought it'd be fun to compile a list of some of your favorite tips and tricks of the trade. Readers were asked to submit their favorite piece of Objective-C trivia, framework arcana, hidden Xcode feature, or anything else they thought is cool."
-redirect_from:
- - /reader-submissions-new-years-2013/
status:
swift: n/a
---
@@ -13,9 +11,7 @@ In celebration of the forthcoming `year++`, I thought it'd be fun to compile a l
Thanks to [Cédric Luthi](https://github.com/0xced), [Jason Kozemczak](https://github.com/jaykz52), [Jeff Kelley](https://github.com/SlaunchaMan), [Joel Parsons](https://github.com/joelparsons), [Maximilian Tagher](https://github.com/MaxGabriel), [Rob Mayoff](https://github.com/mayoff), [Vadim Shpakovski](https://github.com/shpakovski), & [@alextud](https://github.com/alextud) for [answering the call](https://gist.github.com/4148342) with _excellent_ submissions.
-
-Associated Objects in Categories
---------------------------------
+## Associated Objects in Categories
This first tip is so nice it was mentioned twice, both by [Jason Kozemczak](https://github.com/jaykz52) & [Jeff Kelley](https://github.com/SlaunchaMan).
@@ -23,15 +19,15 @@ Categories are a well-known feature of Objective-C, allowing new methods to be a
### NSObject+IndieBandName.h
-~~~{objective-c}
+```objc
@interface NSObject (IndieBandName)
@property (nonatomic, strong) NSString *indieBandName;
@end
-~~~
+```
### NSObject+IndieBandName.m
-~~~{objective-c}
+```objc
#import "NSObject+Extension.h"
#import
@@ -49,7 +45,7 @@ static const void *IndieBandNameKey = &IndieBandNameKey;
}
@end
-~~~
+```
This way, all of your objects can store and retrieve the name of their band, which--by the way--is performing this Wednesday night, and you should totally come.
@@ -57,8 +53,7 @@ While this is a cool trick and all, it should only be used as a method of last r
A good example of an associated object is how [AFNetworking](https://github.com/AFNetworking/AFNetworking) adds a property for an image request operation in its [`UIImageView` category](https://github.com/AFNetworking/AFNetworking/blob/master/AFNetworking/UIImageView%2BAFNetworking.m#L39).
-LLDB View Hierarchy Dump
-------------------------
+## LLDB View Hierarchy Dump
[Rob Mayoff](https://github.com/mayoff) responded with an obscure and powerful incantation to make debugging views a delight. Create `.lldbinit` in your home directory, if it doesn't already exist, and add the following:
@@ -68,9 +63,7 @@ LLDB View Hierarchy Dump
Now you can get a recursive hierarchy of any view in your iOS application with the LLDB debugger. You can try this for yourself by setting a breakpoint in a view controller, and type `rd self.view`. You may be surprised by what's under the hood with some of the built-in UI controls!
-
-LLDB Print Contents of a `CGPathRef`
-------------------------------------
+## LLDB Print Contents of a `CGPathRef`
While we're on the subject of LLDB, [Rob Mayoff](https://github.com/mayoff) sent in a useful incantation for printing out the contents of a `CGPathRef` from the debugger:
@@ -78,8 +71,7 @@ While we're on the subject of LLDB, [Rob Mayoff](https://github.com/mayoff) sent
If you're doing any kind of complex Core Graphics drawing, be sure to keep this one handy.
-Use `+initialize`, Not `+load`
-------------------------------------
+## Use `+initialize`, Not `+load`
[Vadim Shpakovski](https://github.com/shpakovski) wrote in with some advice about class loading and initialization. There are two magical class methods in Objective-C: `+load` and `+initialize`, which are automatically called by virtue of the class being used. The difference between the two methods, however, has significant performance implications for your application.
@@ -91,21 +83,19 @@ Use `+initialize`, Not `+load`
**tl;dr: Implement `+initialize`, not `+load`, if you need this automatic behavior.**
-Xcode Snippets
---------------
+## Xcode Snippets
[Maximilian Tagher](https://github.com/MaxGabriel) gave a shout-out to the benefits of Xcode Snippets.
-Great developers take pride in knowing their tools, and being able to use them to maximum effect. [For better](https://twitter.com/javisoto/status/285531250373046272) or [for worse](http://www.textfromxcode.com), this means knowing Xcode like the back of our hand. Verbose as Objective-C is, "do more by typing less" rings especially true as a productivity mantra, and [Xcode Snippets](http://developer.apple.com/library/mac/#recipes/xcode_help-source_editor/CreatingaCustomCodeSnippet/CreatingaCustomCodeSnippet.html#//apple_ref/doc/uid/TP40009975-CH14-SW1) are one of the best ways to do this.
+Great developers take pride in knowing their tools, and being able to use them to maximum effect. [For better](https://twitter.com/javisoto/status/285531250373046272) or [for worse](http://www.textfromxcode.com), this means knowing Xcode like the back of our hand. Verbose as Objective-C is, "do more by typing less" rings especially true as a productivity mantra, and [Xcode Snippets](https://developer.apple.com/library/mac/#recipes/xcode_help-source_editor/CreatingaCustomCodeSnippet/CreatingaCustomCodeSnippet.html#//apple_ref/doc/uid/TP40009975-CH14-SW1) are one of the best ways to do this.
If you're looking for a place to start, try downloading and forking [these Xcode Snippets](https://github.com/mattt/Xcode-Snippets).
-Macro for Measuring Execution Time
-----------------------------------
+## Macro for Measuring Execution Time
Here's a helpful macro for easily measuring the elapsed time for executing a particular block of code, sent in from [@alextud](https://github.com/alextud):
-~~~{objective-c}
+```objc
NS_INLINE void MVComputeTimeWithNameAndBlock(const char *caller, void (^block)()) {
CFTimeInterval startTimeInterval = CACurrentMediaTime();
block();
@@ -114,26 +104,23 @@ NS_INLINE void MVComputeTimeWithNameAndBlock(const char *caller, void (^block)()
}
#define MVComputeTime(...) MVComputeTimeWithNameAndBlock(__PRETTY_FUNCTION__, (__VA_ARGS__))
-~~~
+```
-Block Enumeration Methods
--------------------------
+## Block Enumeration Methods
[Joel Parsons](https://github.com/joelparsons) submitted a great tip about using `-enumerateObjectsWithOptions:usingBlock:` in `NSArray` and other collection classes. By passing the `NSEnumerationConcurrent` option, you can get significant performance benefits over `NSFastEnumeration`'s `for...in`-style enumeration by executing the block concurrently.
However, be warned! Not all enumerations lend themselves to concurrent execution, so don't go around replacing all of your `for...in` blocks with `NSEnumerationConcurrent` willy-nilly, unless random crashing is something you like in an app.
-Reverse-Engineered Implementation of `NSString` Equality Methods
-----------------------------------------------------------------
+## Reverse-Engineered Implementation of `NSString` Equality Methods
Displaying his characteristic brilliance and familiarity of Cocoa internals [Cédric Luthi](https://github.com/0xced) submitted [a reverse-engineered implementation of the `NString` equality methods](https://gist.github.com/2275014). Fascinating!
-Animate `NSLayoutConstraint.constant`
--------------------------------------
+## Animate `NSLayoutConstraint.constant`
This one goes out to all you fans of [Cocoa Auto Layout](https://developer.apple.com/library/mac/#documentation/UserExperience/Conceptual/AutolayoutPG/Articles/Introduction.html#//apple_ref/doc/uid/TP40010853), from [Vadim Shpakovski](https://github.com/shpakovski):
-~~~{objective-c}
+```objc
viewConstraint.constant = <#Constant Value From#>;
[view layoutIfNeeded];
@@ -143,22 +130,21 @@ viewConstraint.constant = <#Constant Value To#>;
[UIView animateWithDuration:ConstantAnimationDuration animations:^{
[view layoutIfNeeded];
}];
-~~~
+```
Attentive readers may have already noted this, but the code above would make an _excellent_ Xcode Snippet, by the way.
-Printing `NSCache` Usage
-------------------------
+## Printing `NSCache` Usage
-Finishing up this batch of tips and tricks is [Cédric Luthi](https://github.com/0xced) again, this time unearthing the private method `cache_print` as a way to get some visibility into [`NSCache`](http://nshipster.com/nscache/):
+Finishing up this batch of tips and tricks is [Cédric Luthi](https://github.com/0xced) again, this time unearthing the private method `cache_print` as a way to get some visibility into [`NSCache`](https://nshipster.com/nscache/):
-~~~{objective-c}
+```objc
extern void cache_print(void *cache);
- (void) printCache:(NSCache *)cache {
cache_print(*((void **)(__bridge void *)cache + 3));
}
-~~~
+```
This code sample has only been tested on iOS, and should only be used for debugging (i.e. take this out before submitting to Apple!).
diff --git a/2013-01-07-nil.md b/2013-01-07-nil.md
index 017828f5..31c20eac 100644
--- a/2013-01-07-nil.md
+++ b/2013-01-07-nil.md
@@ -1,56 +1,26 @@
---
-title: "nil / Nil / NULL / NSNull"
-author: Mattt Thompson
+title: nil / Nil / NULL / NSNull
+author: Mattt
category: Objective-C
tags: nshipster, popular
-excerpt: "Understanding the concept of nothingness is as much a philosophical issue as it is a pragmatic one. We are inhabitants of a universe of somethings, yet reason in a logical universe of existential uncertainties. As a physical manifestation of a logical system, computers are faced with the intractable problem of how to represent nothing with something."
+excerpt: >-
+ Understanding the concept of nothingness is as much a philosophical issue
+ as it is a pragmatic one.
status:
- swift: n/a
+ swift: n/a
---
-Understanding the concept of nothingness is as much a philosophical issue as it is a pragmatic one. We are inhabitants of a universe of _somethings_, yet reason in a logical universe of existential uncertainties. As a physical manifestation of a logical system, computers are faced with the intractable problem of how to represent _nothing_ with _something_.
+Understanding the concept of nothingness is as much a philosophical issue
+as it is a pragmatic one.
+We are inhabitants of a universe of _somethings_,
+yet reason in a logical universe of ontological uncertainties.
+As a physical manifestation of a logical system,
+computers are faced with the intractable problem
+of how to represent _nothing_ with _something_.
-In Objective-C, there are several different varieties of _nothing_. The reason for this goes back to [a common NSHipster refrain](http://nshipster.com/ns_enum-ns_options/), of how Objective-C bridges the procedural paradigm of C with Smalltalk-inspired object-oriented paradigm.
-
-C represents _nothing_ as `0` for primitive values, and `NULL` for pointers ([which is equivalent to `0` in a pointer context](http://c-faq.com/null/nullor0.html)).
-
-Objective-C builds on C's representation of _nothing_ by adding `nil`. `nil` is an _object_ pointer to nothing. Although semantically distinct from `NULL`, they are technically equivalent to one another.
-
-On the framework level, Foundation defines `NSNull`, which defines a class method, `+null`, which returns the singleton `NSNull` object. `NSNull` is different from `nil` or `NULL`, in that it is an actual object, rather than a zero value.
-
-Additionally, in [Foundation/NSObjCRuntime.h](https://gist.github.com/4469665), `Nil` is defined as a _class_ pointer to nothing. This lesser-known title-case cousin of `nil` doesn't show up much very often, but it's at least worth noting.
-
-## There's Something About `nil`
-
-Newly-`alloc`'d `NSObject`s start life with [their contents set to `0`](https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSObject_Class/Reference/Reference.html). This means that all pointers that object has to other objects begin as `nil`, so it's unnecessary to, for instance, set `self.(association) = nil` in `init` methods.
-
-Perhaps the most notable behavior of `nil`, though, is that it can have messages sent to it.
-
-In other languages, like C++, this would crash your program, but in Objective-C, invoking a method on `nil` returns a zero value. This greatly simplifies expressions, as it obviates the need to check for `nil` before doing anything:
-
-~~~{objective-c}
-// For example, this expression...
-if (name != nil && [name isEqualToString:@"Steve"]) { ... }
-
-// ...can be simplified to:
-if ([name isEqualToString:@"Steve"]) { ... }
-~~~
-
-Being aware of how `nil` works in Objective-C allows this convenience to be a feature, and not a lurking bug in your application. Make sure to guard against cases where `nil` values are unwanted, either by checking and returning early to fail silently, or adding a `NSParameterAssert` to throw an exception.
-
-## `NSNull`: Something for Nothing
-
-`NSNull` is used throughout Foundation and other frameworks to skirt around the limitations of collections like `NSArray` and `NSDictionary` not being able to contain `nil` values. You can think of `NSNull` as effectively [boxing][1] the `NULL` or `nil` value so that it can be used in collections:
-
-~~~{objective-c}
-NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionary];
-mutableDictionary[@"someKey"] = [NSNull null]; // Sets value of NSNull singleton for `someKey`
-NSLog(@"Keys: %@", [mutableDictionary allKeys]); // @[@"someKey"]
-~~~
-
----
-
-So to recap, here are the four values representing _nothing_ that every Objective-C programmer should know about:
+In Objective-C,
+there are a few different varieties of nothing:
+`NULL`, `nil`, `Nil`, and `NSNull`.
@@ -62,26 +32,129 @@ So to recap, here are the four values representing _nothing_ that every Objectiv
-
NULL
-
(void *)0
+
NULL
+
(void *)0
literal null value for C pointers
-
nil
-
(id)0
+
nil
+
(id)0
literal null value for Objective-C objects
-
Nil
-
(Class)0
+
Nil
+
(Class)0
literal null value for Objective-C classes
-
NSNull
-
[NSNull null]
+
NSNull
+
[NSNull null]
singleton object used to represent null
-[1]: http://en.wikipedia.org/wiki/Object_type_(object-oriented_programming)#Boxing
+## NULL
+
+C represents _nothing_ as `0` for primitive values
+and `NULL` for pointers
+_([which is equivalent to `0` in a pointer context](http://c-faq.com/null/nullor0.html))_.
+
+The only time you see `NULL` in Objective-C code
+is when interacting with low-level C APIs
+like Core Foundation or Core Graphics.
+
+## nil
+
+Objective-C builds on C's representation of _nothing_ by adding `nil`.
+`nil` is an _object_ pointer to nothing.
+Although semantically distinct from `NULL`,
+they are technically equivalent.
+
+{% info %}
+Newly-`alloc`'d `NSObject`s
+[start life with their contents set to `0`](https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSObject_Class/Reference/Reference.html).
+For this reason,
+it's unnecessary to set an object's non-primitive properties to `nil`
+in its initializer.
+{% endinfo %}
+
+Perhaps the most notable behavior of `nil`,
+however, is that it can have messages sent to it without a problem.
+In other languages, like Java, this kind of behavior would crash your program.
+But in Objective-C,
+invoking a method on `nil` returns a zero value ---
+which is to say, _"`nil` begets `nil`"_.
+This fact alone significantly simplifies things for Objective-C developers,
+as it obviates the need to check for `nil` before doing anything:
+
+```objc
+// For example, this expression...
+if (name != nil && [name isEqualToString:@"Steve"]) { ... }
+
+// ...can be simplified to:
+if ([name isEqualToString:@"Steve"]) { ... }
+```
+
+With vigilance, this quirk of Objective-C can be a convenient feature
+rather than a lurking source of bugs in your application.
+
+{% warning %}
+Make sure to guard against cases where `nil` values are unwanted,
+either with a precondition assertion (`NSParameterAssert`)
+or by checking explicitly and returning early.
+{% endwarning %}
+
+## Nil
+
+In addition to `nil`,
+[the Objective-C runtime defines `Nil`](https://gist.github.com/4469665)
+as a _class_ pointer to nothing.
+This lesser-known title-case cousin of `nil` doesn't show up much very often,
+but it's at least worth noting.
+
+## NSNull
+
+On the framework level,
+Foundation defines `NSNull`.
+`NSNull` defines a class method (`+null`)
+that returns the singleton `NSNull` object.
+`NSNull` represents nothing with an actual object
+rather than a zero value like `nil` or `NULL`.
+
+`NSNull` is used throughout Foundation and other frameworks
+to skirt around the limitations of collections
+like `NSArray` and `NSDictionary` not being able to contain `nil` values.
+You can think of `NSNull` as effectively [boxing][boxing]
+the `NULL` or `nil` value so that it can be used in collections:
+
+```objc
+NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionary];
+mutableDictionary[@"someKey"] = [NSNull null];
+NSLog(@"%@", [mutableDictionary allKeys]);
+// @[@"someKey"]
+```
+
+* * *
+
+Why does Objective-C have four values for nothing when one might suffice?
+It all goes back to
+[a common refrain](/ns_enum-ns_options/),
+about how Objective-C bridges the procedural paradigm of C
+with Smalltalk-inspired object-oriented paradigm.
+
+In the procedural world of C,
+values are indistinguishable from their numeric value.
+The same zero value might represent
+the first index of an array,
+the terminating byte of a string, or
+the simply result of "2 - 2".
+Objective-C constructs an object-oriented layer on top of these primitives,
+and in so doing establishes a distinct logical universe.
+This abstraction allows for a sentinel value,
+the `NSNull` singleton,
+to be defined independently of numbers.
+`nil` (and `Nil`) therefore serve as intermediaries between these worlds
+at their convergence point: zero.
+
+[boxing]: https://en.wikipedia.org/wiki/Object_type_(object-oriented_programming)#Boxing
diff --git a/2013-01-14-__attribute__.md b/2013-01-14-__attribute__.md
index 38493060..555a0faf 100644
--- a/2013-01-14-__attribute__.md
+++ b/2013-01-14-__attribute__.md
@@ -1,6 +1,6 @@
---
title: "__attribute__"
-author: Mattt Thompson
+author: Mattt
category: Objective-C
tags: nshipster
excerpt: "A recurring theme of this publication has been the importance of a healthy relationship with the compiler. Like any craft, one's effectiveness as a practitioner is contingent on how they treat their tools. Take good care of them, and they'll take good care of you."
@@ -14,7 +14,7 @@ A recurring theme of this publication has been the importance of a healthy relat
The syntax for this keyword is `__attribute__` followed by two sets of parentheses (the double parentheses makes it easy to "macro out", especially with multiple attributes). Inside the parentheses is a comma-delimited list of attributes. `__attribute__` directives are placed after function, variable, and type declarations.
-~~~{objective-c}
+```objc
// Return the square of a number
int square(int n) __attribute__((const));
@@ -25,9 +25,9 @@ void f(void)
// Send printf-like message to stderr and exit
extern void die(const char *format, ...)
__attribute__((noreturn, format(printf, 1, 2)));
-~~~
+```
-If this is starting to remind you of ISO C's [`#pragma`](http://nshipster.com/pragma), you're not alone.
+If this is starting to remind you of ISO C's [`#pragma`](https://nshipster.com/pragma), you're not alone.
In fact, when `__attribute__` was first introduced to GCC, it was faced with some resistance by some who suggested that `#pragma` be used exclusively for the same purposes.
@@ -53,11 +53,11 @@ GCC
> The `format` attribute specifies that a function takes `printf`, `scanf`, `strftime` or `strfmon` style arguments which should be type-checked against a format string.
-~~~{objective-c}
+```objc
extern int
my_printf (void *my_object, const char *my_format, ...)
__attribute__((format(printf, 2, 3)));
-~~~
+```
Objective-C programmers can also use the `__NSString__` format to enforce the same rules as format strings in `NSString +stringWithFormat:` and `NSLog()`.
@@ -65,11 +65,11 @@ Objective-C programmers can also use the `__NSString__` format to enforce the sa
> The `nonnull` attribute specifies that some function parameters should be non-null pointers.
-~~~{objective-c}
+```objc
extern void *
my_memcpy (void *dest, const void *src, size_t len)
__attribute__((nonnull (1, 2)));
-~~~
+```
Using `nonnull` encodes expectations about values into an explicit contract, which can help catch any `NULL` pointer bugs lurking in any calling code. Remember: compile-time errors ≫ run-time errors.
@@ -85,9 +85,9 @@ For example, AFNetworking [uses the `noreturn` attribute for its network request
> The `const` attribute specifies that a function does not examine any values except their arguments, and have no effects except the return value. Note that a function that has pointer arguments and examines the data pointed to must not be declared const. Likewise, a function that calls a non-`const` function usually must not be `const`. It does not make sense for a `const` function to return `void`.
-~~~{objective-c}
+```objc
int square(int n) __attribute__((const));
-~~~
+```
`pure` and `const` are both attributes that invoke a functional programming paradigm in order to allow for significant performance optimizations. `const` can be thought as a stricter form of `pure` since it doesn't depend on global values or pointers.
@@ -110,9 +110,9 @@ To check the availability of a particular attribute, you can use the `__has_attr
> Clang introduces the availability attribute, which can be placed on declarations to describe the lifecycle of that declaration relative to operating system versions. Consider the function declaration for a hypothetical function f:
-~~~{objective-c}
+```objc
void f(void) __attribute__((availability(macosx,introduced=10.4,deprecated=10.6,obsoleted=10.7)));
-~~~
+```
> The `availability` attribute states that `f` was introduced in OS X Tiger, deprecated in OS X Snow Leopard, and obsoleted in OS X Lion.
@@ -137,12 +137,12 @@ Supported Platforms:
> Clang provides support for C++ function overloading in C. Function overloading in C is introduced using the `overloadable` attribute. For example, one might provide several overloaded versions of a `tgsin` function that invokes the appropriate standard function computing the `sine` of a value with `float`, `double`, or `long double` precision:
-~~~{objective-c}
+```objc
#include
float __attribute__((overloadable)) tgsin(float x) { return sinf(x); }
double __attribute__((overloadable)) tgsin(double x) { return sin(x); }
long double __attribute__((overloadable)) tgsin(long double x) { return sinl(x); }
-~~~
+```
Note that `overloadable` only works for functions. You can overload method declarations to some extent by using generic return and parameter types, like `id` and `void *`.
diff --git a/2013-01-21-nslocalizedstring.md b/2013-01-21-nslocalizedstring.md
index 598aba21..5ef9fba6 100644
--- a/2013-01-21-nslocalizedstring.md
+++ b/2013-01-21-nslocalizedstring.md
@@ -1,6 +1,6 @@
---
title: NSLocalizedString
-author: Mattt Thompson
+author: Mattt
category: Cocoa
tags: nshipster
excerpt: "Strings are perhaps the most versatile data type in computing. They're passed around as symbols, used to encode numeric values, associate values to keys, represent resource paths, store linguistic content, and format information. Having a strong handle on user-facing strings is essential to making a great user experience."
@@ -14,7 +14,7 @@ In Foundation, there is a convenient macro for denoting strings as user-facing:
`NSLocalizedString` provides string localization in "compile-once / run everywhere" fashion, replacing all localized strings with their respective translation according to the string tables of the user settings. But even if you're not going to localize your app to any other markets, `NSLocalizedString` does wonders with respect to copy writing & editing.
-> For more information about Localization (l10n) and Internationalization (i18n) [see the NSHipster article about NSLocale](http://nshipster.com/nslocale/).
+> For more information about Localization (l10n) and Internationalization (i18n) [see the NSHipster article about NSLocale](https://nshipster.com/nslocale/).
---
@@ -22,23 +22,23 @@ In Foundation, there is a convenient macro for denoting strings as user-facing:
In practice, the `key` is often just the base translation string to be used, while `comment` is usually `nil`, unless there is an ambiguous context:
-~~~{objective-c}
+```objc
textField.placeholder = NSLocalizedString(@"Username", nil);
-~~~
+```
`NSLocalizedString` can also be used as a format string in `NSString +stringWithFormat:`. In these cases, it's important to use the `comment` argument to provide enough context to be properly translated.
-~~~{objective-c}
+```objc
self.title = [NSString stringWithFormat:NSLocalizedString(@"%@'s Profile", @"{User First Name}'s Profile"), user.name];
label.text = [NSString stringWithFormat:NSLocalizedString(@"Showing %lu of %lu items", @"Showing {number} of {total number} items"), [page count], [items count]];
-~~~
+```
## `NSLocalizedString` & Co.
There are four varieties of `NSLocalizedString`, with increasing levels of control (and obscurity):
-~~~{objective-c}
+```objc
NSString * NSLocalizedString(
NSString *key,
NSString *comment
@@ -64,7 +64,7 @@ NSString * NSLocalizedStringWithDefaultValue(
NSString *value,
NSString *comment
)
-~~~
+```
99% of the time, `NSLocalizedString` will suffice. If you're working in a library or shared component, `NSLocalizedStringFromTable` should be used instead.
@@ -74,12 +74,12 @@ At runtime, `NSLocalizedString` determines the preferred language, and finds a c
Here's what that looks like:
-~~~
+```
/* No comment provided by engineer. */
"Username"="nom d'utilisateur";
/* {User First Name}'s Profile */
"%@'s Profile"="profil de %1$@";
-~~~
+```
`Localizable.strings` files are initially generated with `genstrings`.
diff --git a/2013-01-28-nsvalue.md b/2013-01-28-nsvalue.md
index 95811ead..ceb8fbaa 100644
--- a/2013-01-28-nsvalue.md
+++ b/2013-01-28-nsvalue.md
@@ -1,6 +1,6 @@
---
title: NSValue
-author: Mattt Thompson
+author: Mattt
category: Cocoa
tags: nshipster
excerpt: "Boxing is the process of encapsulating scalars and value types with an object container, and is primarily used to store those values in collection objects—namely arrays and dictionaries. In Foundation, the reigning featherweight champion of boxing is NSValue."
@@ -16,7 +16,7 @@ Yet, this is _exactly_ what we do as Objective-C programmers.
...well not _exactly_, but there is a box involved.
-As mentioned [time](http://nshipster.com/ns_enum-ns_options/) and [again](http://nshipster.com/nil/) in NSHipster, what makes Objective-C such a curiosity is the way it merges the old, procedural world of C with the modern Object-Oriented influences of Smalltalk. When done correctly, this tension can be exploited to craft semantically rich software without sacrificing performance. But bridging that gap between old and new is a miasma of casts, bridges, and of course, boxes.
+As mentioned [time](https://nshipster.com/ns_enum-ns_options/) and [again](https://nshipster.com/nil/) in NSHipster, what makes Objective-C such a curiosity is the way it merges the old, procedural world of C with the modern Object-Oriented influences of Smalltalk. When done correctly, this tension can be exploited to craft semantically rich software without sacrificing performance. But bridging that gap between old and new is a miasma of casts, bridges, and of course, boxes.
Boxing is the process of encapsulating scalars (`int`, `double`, `BOOL`, etc.) and value types (`struct`, `enum`) with an object container, and is primarily used to store those values in collection objects—namely arrays and dictionaries.
@@ -37,25 +37,25 @@ While boxing is an admittedly dry subject matter, there are two methods in parti
> - `value`: The value for the new `NSValue` object.
> - `type`: The Objective-C type of value. `type` should be created with the Objective-C `@encode()` compiler directive; it should not be hard-coded as a C string.
-`@encode` was discussed in [our rundown of the myriad `@` Compiler Directives](http://nshipster.com/at-compiler-directives/):
+`@encode` was discussed in [our rundown of the myriad `@` Compiler Directives](https://nshipster.com/at-compiler-directives/):
-> `@encode()`: Returns the [type encoding](http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html) of a type. This type value can be used as the first argument encode in `NSCoder -encodeValueOfObjCType:at`.
+> `@encode()`: Returns the [type encoding](https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html) of a type. This type value can be used as the first argument encode in `NSCoder -encodeValueOfObjCType:at`.
-The subject of type encodings would make for [a great article of its own](http://nshipster.com/type-encodings/), but the main takeaway is that it serves as a terse, human-readable representation of the structure of a type. For example,
+The subject of type encodings would make for [a great article of its own](https://nshipster.com/type-encodings/), but the main takeaway is that it serves as a terse, human-readable representation of the structure of a type. For example,
-~~~{objective-c}
+```objc
typedef struct example {
id anObject;
char *aString;
int anInt;
} Example;
-~~~
+```
...has the encoding:
-~~~{objective-c}
+```objc
{example=@*i}
-~~~
+```
`NSValue` uses this type encoding to create the necessary data structures to represent these values internally. Neat!
diff --git a/2013-02-04-type-encodings.md b/2013-02-04-type-encodings.md
index 7041de3f..ac0b45b6 100644
--- a/2013-02-04-type-encodings.md
+++ b/2013-02-04-type-encodings.md
@@ -1,6 +1,6 @@
---
title: Type Encodings
-author: Mattt Thompson
+author: Mattt
category: Objective-C
tags: nshipster
excerpt: "From number stations and numerology to hieroglyphics and hobo codes, there is something truly fascinating about finding meaning that hides in plain sight. Though hidden messages in and of themselves are rarely useful or particularly interesting, it's the thrill of the hunt that piques our deepest curiosities."
@@ -8,15 +8,15 @@ status:
swift: n/a
---
-From [number stations](http://en.wikipedia.org/wiki/Numbers_station) and [numerology](http://en.wikipedia.org/wiki/Numerology) to [hieroglyphs](http://en.wikipedia.org/wiki/Egyptian_hieroglyphs) and [hobo codes](http://en.wikipedia.org/wiki/Hobo#Hobo_.28sign.29_code), there is something truly fascinating about finding meaning that hides in plain sight. Though hidden messages in and of themselves are rarely useful or particularly interesting, it's the thrill of the hunt that piques our deepest curiosities.
+From [number stations](https://en.wikipedia.org/wiki/Numbers_station) and [numerology](https://en.wikipedia.org/wiki/Numerology) to [hieroglyphs](https://en.wikipedia.org/wiki/Egyptian_hieroglyphs) and [hobo codes](https://en.wikipedia.org/wiki/Hobo#Hobo_.28sign.29_code), there is something truly fascinating about finding meaning that hides in plain sight. Though hidden messages in and of themselves are rarely useful or particularly interesting, it's the thrill of the hunt that piques our deepest curiosities.
It is in this spirit that we take a look at [Objective-C Type Encodings](https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html) in this week's edition of NSHipster.
---
-[Last week](http://nshipster.com/nsvalue/), in a discussion about `NSValue`, there was mention of `+valueWithBytes:objCType:`, whose second parameter should be created with the Objective-C `@encode()` compiler directive.
+[Last week](https://nshipster.com/nsvalue/), in a discussion about `NSValue`, there was mention of `+valueWithBytes:objCType:`, whose second parameter should be created with the Objective-C `@encode()` compiler directive.
-`@encode`, one of the [`@` Compiler Directives](http://nshipster.com/at-compiler-directives/), returns a C string that encodes the internal representation of a given type (e.g., `@encode(int)` → `i`), similar to the ANSI C `typeof` operator. Apple's Objective-C runtime uses type encodings internally to help facilitate message dispatching.
+`@encode`, one of the [`@` Compiler Directives](https://nshipster.com/at-compiler-directives/), returns a C string that encodes the internal representation of a given type (e.g., `@encode(int)` → `i`), similar to the ANSI C `typeof` operator. Apple's Objective-C runtime uses type encodings internally to help facilitate message dispatching.
Here's a rundown of all of the different Objective-C Type Encodings:
@@ -30,60 +30,60 @@ Here's a rundown of all of the different Objective-C Type Encodings:
-
c
-
A char
+
c
+
A char
-
i
-
An int
+
i
+
An int
-
s
-
A short
+
s
+
A short
-
l
-
A longl is treated as a 32-bit quantity on 64-bit programs.
+
l
+
A longl is treated as a 32-bit quantity on 64-bit programs.
-
q
-
A long long
+
q
+
A long long
-
C
-
An unsigned char
+
C
+
An unsigned char
-
I
-
An unsigned int
+
I
+
An unsigned int
-
S
-
An unsigned short
+
S
+
An unsigned short
-
L
-
An unsigned long
+
L
+
An unsigned long
-
Q
-
An unsigned long long
+
Q
+
An unsigned long long
-
f
-
A float
+
f
+
A float
-
d
-
A double
+
d
+
A double
-
B
-
A C++ bool or a C99 _Bool
+
B
+
A C++ bool or a C99 _Bool
-
v
-
A void
+
v
+
A void
-
*
-
A character string (char *)
+
*
+
A character string (char *)
-
@
-
An object (whether statically typed or typed id)
+
@
+
An object (whether statically typed or typed id)
-
#
-
A class object (Class)
+
#
+
A class object (Class)
-
:
-
A method selector (SEL)
+
:
+
A method selector (SEL)
[array type]
An array
@@ -94,21 +94,22 @@ Here's a rundown of all of the different Objective-C Type Encodings:
(name=type...)
A union
-
bnum
+
bnum
A bit field of num bits
-
^type
+
^type
A pointer to type
-
?
+
?
An unknown type (among other things, this code is used for function pointers)
-Of course, charts are fine, but experimenting in code is even better:
+Charts are fine,
+but experimenting in code is even better:
-~~~{objective-c}
+```objc
NSLog(@"int : %s", @encode(int));
NSLog(@"float : %s", @encode(float));
NSLog(@"float * : %s", @encode(float*));
@@ -135,7 +136,7 @@ typedef struct _struct {
unsigned long long c;
} Struct;
NSLog(@"struct : %s", @encode(typeof(Struct)));
-~~~
+```
Result:
@@ -161,7 +162,7 @@ There are some interesting takeaways from this:
- Whereas the standard encoding for pointers is a preceding `^`, `char *` gets its own code: `*`. This makes sense conceptually, as C strings are thought to be entities in and of themselves, rather than a pointer to something else.
- `BOOL` is `c`, rather than `i`, as one might expect. Reason being, `char` is smaller than an `int`, and when Objective-C was originally designed in the 80's, bits (much like the dollar) were more valuable than they are today. `BOOL` is specifically a `signed char` (even if `-funsigned-char` is set), to ensure a consistent type between compilers, since `char` could be either `signed` or `unsigned`.
-- Passing `NSObject` directly yields `#`. However, passing `[NSObject class]` yields a struct named `NSObject` with a single class field. That is, of course, the `isa` field, which all `NSObject` instances have to signify their type.
+- Passing `NSObject` directly yields `#`. However, passing `[NSObject class]` yields a struct named `NSObject` with a single class field: `isa`, which `NSObject` instances have to signify their type.
## Method Encodings
@@ -179,32 +180,32 @@ These are the type qualifiers for methods declared in a protocol:
-
r
-
const
+
r
+
const
-
n
-
in
+
n
+
in
-
N
-
inout
+
N
+
inout
-
o
-
out
+
o
+
out
-
O
-
bycopy
+
O
+
bycopy
-
R
-
byref
+
R
+
byref
-
V
-
oneway
+
V
+
oneway
diff --git a/2013-02-11-nsurlcache.md b/2013-02-11-nsurlcache.md
index b6fc4a79..9b256ca7 100644
--- a/2013-02-11-nsurlcache.md
+++ b/2013-02-11-nsurlcache.md
@@ -1,6 +1,6 @@
---
title: NSURLCache
-author: Mattt Thompson
+author: Mattt
category: Cocoa
excerpt: "NSURLCache provides a composite in-memory and on-disk caching mechanism for URL requests to your application. As part of Foundation's URL Loading System, any request loaded through NSURLConnection will be handled by NSURLCache."
status:
@@ -19,16 +19,16 @@ As of iOS 5, a shared `NSURLCache` is set for the application by default. [Quoth
Those having such special caching requirements can set a shared URL cache in `-application:didFinishLaunchingWithOptions:` on iOS, or `–applicationDidFinishLaunching:` on OS X:
-~~~{swift}
+```swift
func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
let URLCache = NSURLCache(memoryCapacity: 4 * 1024 * 1024, diskCapacity: 20 * 1024 * 1024, diskPath: nil)
NSURLCache.setSharedURLCache(URLCache)
return true
}
-~~~
+```
-~~~{objective-c}
+```objc
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
@@ -37,7 +37,7 @@ func application(application: UIApplication!, didFinishLaunchingWithOptions laun
diskPath:nil];
[NSURLCache setSharedURLCache:URLCache];
}
-~~~
+```
Caching policies are specified in both the request (by the client) and in the response (by the server). Understanding these policies and how they relate to one another is essential to finding the optimal behavior for your application.
@@ -54,7 +54,7 @@ Caching policies are specified in both the request (by the client) and in the re
It may not surprise you that these values are poorly understood and often confused with one another.
-Adding to the confusion is the fact that `NSURLRequestReloadIgnoringLocalAndRemoteCacheData` and `NSURLRequestReloadRevalidatingCacheData` [_aren't even implemented_](https://gist.github.com/mattt/4753073#file-nsurlrequest-h-L95-L108)! ([Link to Radar](http://openradar.appspot.com/radar?id=1755401)).
+Adding to the confusion is the fact that `NSURLRequestReloadIgnoringLocalAndRemoteCacheData` and `NSURLRequestReloadRevalidatingCacheData` [_were not even implemented until iOS 13_](https://developer.apple.com/documentation/ios-ipados-release-notes/ios-13-release-notes)!
So here's what you _actually_ need to know about `NSURLRequestCachePolicy`:
@@ -67,28 +67,28 @@ So here's what you _actually_ need to know about `NSURLRequestCachePolicy`:
-
UseProtocolCachePolicy
+
useProtocolCachePolicy
Default behavior
-
ReloadIgnoringLocalCacheData
+
reloadIgnoringLocalCacheData
Don't use the cache
-
ReloadIgnoringLocalAndRemoteCacheData
-
Seriously, don't use the cache
+
reloadIgnoringLocalAndRemoteCacheData
+
Seriously, don't use any caches along the way
-
ReturnCacheDataElseLoad
+
returnCacheDataElseLoad
Use the cache (no matter how out of date), or if no cached response exists, load from the network
-
ReturnCacheDataDontLoad
+
returnCacheDataDontLoad
Offline mode: use the cache (no matter how out of date), but don't load from the network
-
ReloadRevalidatingCacheData
-
Validate cache against server before using
+
reloadRevalidatingCacheData
+
Validate cache against server before using
@@ -116,7 +116,7 @@ In addition to `Cache-Control`, a server may send additional headers that can be
* `Last-Modified` - The value of this header corresponds to the date and time when the requested resource was last changed. For example, if a client requests a timeline of recent photos, `/photos/timeline`, the `Last-Modified`
value could be set to when the most recent photo was taken.
-* `Etag` - An abbreviation for "entity tag", this is an identifier that represents the contents requested resource. In practice, an `Etag` header value could be something like the [`MD5`](http://en.wikipedia.org/wiki/MD5) digest of the resource properties. This is particularly useful for dynamically generated resources that may not have an obvious `Last-Modified` value.
+* `Etag` - An abbreviation for "entity tag", this is an identifier that represents the contents requested resource. In practice, an `Etag` header value could be something like the [`MD5`](https://en.wikipedia.org/wiki/MD5) digest of the resource properties. This is particularly useful for dynamically generated resources that may not have an obvious `Last-Modified` value.
## `NSURLConnectionDelegate`
@@ -126,7 +126,7 @@ Once the server response has been received, the `NSURLConnection` delegate has a
In `-connection:willCacheResponse:`, the `cachedResponse` object has been automatically created from the result of the URL connection. Because there is no mutable counterpart to `NSCachedURLResponse`, in order to change anything about `cachedResponse`, a new object must be constructed, passing any modified values into `–initWithResponse:data:userInfo:storagePolicy:`, for instance:
-~~~{swift}
+```swift
// MARK: NSURLConnectionDataDelegate
func connection(connection: NSURLConnection!, willCacheResponse cachedResponse: NSCachedURLResponse!) -> NSCachedURLResponse! {
@@ -134,13 +134,13 @@ func connection(connection: NSURLConnection!, willCacheResponse cachedResponse:
var mutableData = NSMutableData(data: cachedResponse.data)
var storagePolicy: NSURLCacheStoragePolicy = .AllowedInMemoryOnly
- // ...
+ <#...#>
return NSCachedURLResponse(response: cachedResponse.response, data: mutableData, userInfo: mutableUserInfo, storagePolicy: storagePolicy)
}
-~~~
+```
-~~~{objective-c}
+```objc
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
willCacheResponse:(NSCachedURLResponse *)cachedResponse
{
@@ -148,36 +148,36 @@ func connection(connection: NSURLConnection!, willCacheResponse cachedResponse:
NSMutableData *mutableData = [[cachedResponse data] mutableCopy];
NSURLCacheStoragePolicy storagePolicy = NSURLCacheStorageAllowedInMemoryOnly;
- // ...
+ <#...#>
return [[NSCachedURLResponse alloc] initWithResponse:[cachedResponse response]
data:mutableData
userInfo:mutableUserInfo
storagePolicy:storagePolicy];
}
-~~~
+```
If `-connection:willCacheResponse:` returns `nil`, the response will not be cached.
-~~~{swift}
+```swift
func connection(connection: NSURLConnection!, willCacheResponse cachedResponse: NSCachedURLResponse!) -> NSCachedURLResponse! {
return nil
}
-~~~
+```
-~~~{objective-c}
+```objc
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
willCacheResponse:(NSCachedURLResponse *)cachedResponse
{
return nil;
}
-~~~
+```
When left unimplemented, `NSURLConnection` will simply use the cached response that would otherwise be passed into `-connection:willCacheResponse:`, so unless you need to change or prevent caching, this method does not need to be implemented in the delegate.
## Caveats
-Just like its unrelated-but-similarly-named cohort, [`NSCache`](http://nshipster.com/nscache/), `NSURLCache` is not without some peculiarities.
+Just like its unrelated-but-similarly-named cohort, [`NSCache`](https://nshipster.com/nscache/), `NSURLCache` is not without some peculiarities.
As of iOS 5, disk caching is supported, but only for HTTP, not HTTPS, requests (though iOS 6 added support for this). Peter Steinberger [wrote an excellent article on this subject](http://petersteinberger.com/blog/2012/nsurlcache-uses-a-disk-cache-as-of-ios5/), after digging into the internals while implementing [his own NSURLCache subclass](https://github.com/steipete/SDURLCache).
diff --git a/2013-02-18-reactivecocoa.md b/2013-02-18-reactivecocoa.md
index 4772d848..559c80b9 100644
--- a/2013-02-18-reactivecocoa.md
+++ b/2013-02-18-reactivecocoa.md
@@ -1,17 +1,18 @@
---
title: ReactiveCocoa
-author: Mattt Thompson
+author: Mattt
category: Open Source
excerpt: "Breaking from a tradition of covering Apple APIs exclusively, this edition of NSHipster will look at an open source project that exemplifies a brave new era of open source contribution to Objective-C: ReactiveCocoa."
+retired: true
status:
- swift: n/a
+ swift: n/a
---
Languages are living works. They are nudged and challenged and bastardized and mashed-up in a perpetual cycle of undirected and rapid evolution. Technologies evolve, requirements change, corporate stewards and open source community come and go; obscure dialects are vaulted to prominence on the shoulders of exciting new frameworks, and thrust into a surprising new context after a long period of dormancy.
Objective-C has a remarkable history spanning four acts in as many decades:
-**In its 1st act**, Objective-C was adopted as the language of NeXT, powering [NeXTSTEP](http://en.wikipedia.org/wiki/NeXTSTEP) and [the world's first web server](http://en.wikipedia.org/wiki/Web_server#History).
+**In its 1st act**, Objective-C was adopted as the language of NeXT, powering [NeXTSTEP](https://en.wikipedia.org/wiki/NeXTSTEP) and [the world's first web server](https://en.wikipedia.org/wiki/Web_server#History).
**In its 2nd act**, Objective-C positioned itself in the heart Apple's technology stack (after a prolonged turf war with Java) with Apple's acquisition of NeXT.
@@ -27,7 +28,7 @@ Breaking from a tradition of covering Apple APIs exclusively, this edition of NS
[ReactiveCocoa](https://github.com/ReactiveCocoa/ReactiveCocoa) is an open source library that brings Functional Reactive Programming paradigm to Objective-C. It was created by [Josh Abernathy](https://github.com/joshaber) & [Justin Spahr-Summers](https://github.com/jspahrsummers) in the development of [GitHub for Mac](http://mac.github.com). Last week, ReactiveCocoa reached a major milestone with its [1.0 release](https://github.com/ReactiveCocoa/ReactiveCocoa/tree/v1.0.0).
-[Functional Reactive Programming](http://en.wikipedia.org/wiki/Functional_reactive_programming) (FRP) is a way of thinking about software in terms of transforming inputs to produce output continuously over time. [Josh Abernathy frames the paradigm thusly](http://blog.maybeapps.com/post/42894317939/input-and-output):
+[Functional Reactive Programming](https://en.wikipedia.org/wiki/Functional_reactive_programming) (FRP) is a way of thinking about software in terms of transforming inputs to produce output continuously over time. [Josh Abernathy frames the paradigm thusly](http://blog.maybeapps.com/post/42894317939/input-and-output):
> Programs take input and produce output. The output is the result of doing something with the input. Input, transform, output, done.
>
@@ -41,7 +42,7 @@ To illustrate the difference between the conventional, imperative paradigm of Ob
### Conventional
-~~~{objective-c}
+```objc
- (BOOL)isFormValid {
return [self.usernameField.text length] > 0 &&
[self.emailField.text length] > 0 &&
@@ -59,7 +60,7 @@ replacementString:(NSString *)string
return YES;
}
-~~~
+```
In the conventional example, logic is fragmented across different methods in the view controller, with calls to `self.createButton.enabled = [self isFormValid];` interspersed throughout delegate methods and view lifecycle callbacks.
@@ -67,7 +68,7 @@ Compare this with equivalent code using ReactiveCocoa:
### ReactiveCocoa
-~~~{objective-c}
+```objc
RACSignal *formValid = [RACSignal
combineLatest:@[
self.username.rac_textSignal,
@@ -80,7 +81,7 @@ RACSignal *formValid = [RACSignal
}];
RAC(self.createButton.enabled) = formValid;
-~~~
+```
Here, all of the logic for validating form input is contained in a single chain of logic and responsibility. Each time any of the text fields is updated, their inputs are reduced into a single boolean value, which automatically enables / disables the create button.
@@ -98,31 +99,31 @@ Both signals and sequences are kinds of [streams](https://github.com/ReactiveCoc
> Signals send three different types of events to their subscribers:
>
-> * The **next** event provides a new value from the stream. Unlike Cocoa collections, it is
- completely valid for a signal to include `nil`.
-> * The **error** event indicates that an error occurred before the signal could
- finish. The event may include an `NSError` object that indicates what went
- wrong. Errors must be handled specially – they are not included in the
- stream's values.
-> * The **completed** event indicates that the signal finished successfully, and
- that no more values will be added to the stream. Completion must be handled
- specially – it is not included in the stream of values.
+> - The **next** event provides a new value from the stream. Unlike Cocoa collections, it is
+> completely valid for a signal to include `nil`.
+> - The **error** event indicates that an error occurred before the signal could
+> finish. The event may include an `NSError` object that indicates what went
+> wrong. Errors must be handled specially – they are not included in the
+> stream's values.
+> - The **completed** event indicates that the signal finished successfully, and
+> that no more values will be added to the stream. Completion must be handled
+> specially – it is not included in the stream of values.
>
> The lifetime of a signal consists of any number of `next` events, followed by
-one `error` or `completed` event (but not both).
+> one `error` or `completed` event (but not both).
### `RACSequence`
> - **Simplifying Collection Transformations**: Higher-order functions like `map`, `filter`, `fold/reduce` are sorely missing from `Foundation`.
> Sequences are a kind of collection, similar in purpose to `NSArray`. Unlike
-an array, the values in a sequence are evaluated _lazily_ (i.e., only when they
-are needed) by default, potentially improving performance if only part of
-a sequence is used. Just like Cocoa collections, sequences cannot contain `nil`.
+> an array, the values in a sequence are evaluated _lazily_ (i.e., only when they
+> are needed) by default, potentially improving performance if only part of
+> a sequence is used. Just like Cocoa collections, sequences cannot contain `nil`.
>
> `RACSequence` allows any Cocoa collection to be manipulated in a uniform and declarative way.
-~~~{objective-c}
+```objc
RACSequence *normalizedLongWords = [[words.rac_sequence
filter:^ BOOL (NSString *word) {
return [word length] >= 10;
@@ -130,7 +131,7 @@ RACSequence *normalizedLongWords = [[words.rac_sequence
map:^(NSString *word) {
return [word lowercaseString];
}];
-~~~
+```
## Precedents in Cocoa
@@ -138,7 +139,7 @@ Capturing and responding to changes has a long tradition in Cocoa, and ReactiveC
### RAC vs. KVO
-[Key-Value Observing](http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html) is at the heart of all magic in Cocoa—indeed, it is used extensively by ReactiveCocoa to react to property changes. However, KVO is neither pleasant nor easy to use: its API is overwrought with unused parameters and sorely lacking a blocks-based interface.
+[Key-Value Observing](https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html) is at the heart of all magic in Cocoa—indeed, it is used extensively by ReactiveCocoa to react to property changes. However, KVO is neither pleasant nor easy to use: its API is overwrought with unused parameters and sorely lacking a blocks-based interface.
### RAC vs. Bindings
@@ -150,11 +151,11 @@ Although essential to managing the complexity of a OS X application, Bindings' c
Objective-C was built from Smalltalk's ideas on top of C's metal, but its cultural imports go far beyond its original pedigree.
-`@protocol` was a rejection of C++'s multiple inheritance, favoring an abstract data type pattern comparable to a Java `Interface`. Objective-C 2.0 introduced `@property / @synthesize`, a contemporary of C#'s `get; set;` shorthand for getter and setter methods (as well as dot syntax, which is still a point of contention for NeXTSTEP hard-liners). Blocks injected some functional programming flavor to the language, which paired nicely with Grand Central Dispatch--a queue-based concurrency API almost certainly influenced by Fortran / C / C++ standard [OpenMP](http://en.wikipedia.org/wiki/OpenMP). Subscripting and object literals, a standard feature in scripting languages like Ruby and Javascript, now finally brought to Objective-C thanks to a Clang language extension.
+`@protocol` was a rejection of C++'s multiple inheritance, favoring an abstract data type pattern comparable to a Java `Interface`. Objective-C 2.0 introduced `@property / @synthesize`, a contemporary of C#'s `get; set;` shorthand for getter and setter methods (as well as dot syntax, which is still a point of contention for NeXTSTEP hard-liners). Blocks injected some functional programming flavor to the language, which paired nicely with Grand Central Dispatch--a queue-based concurrency API almost certainly influenced by Fortran / C / C++ standard [OpenMP](https://en.wikipedia.org/wiki/OpenMP). Subscripting and object literals, a standard feature in scripting languages like Ruby and Javascript, now finally brought to Objective-C thanks to a Clang language extension.
-ReactiveCocoa brings a healthy dose of functional and reactive programming influence to Objective-C, and was itself influenced by C#'s [Rx library](http://msdn.microsoft.com/en-us/data/gg577609.aspx), [Clojure](http://en.wikipedia.org/wiki/Clojure), and [Elm][2].
+ReactiveCocoa brings a healthy dose of functional and reactive programming influence to Objective-C, and was itself influenced by C#'s [Rx library](http://msdn.microsoft.com/en-us/data/gg577609.aspx), [Clojure](https://en.wikipedia.org/wiki/Clojure), and [Elm][2].
Good ideas are contagious. ReactiveCocoa is a reminder that good ideas can come from unlikely places, and that a fresh perspective can make all of the difference with familiar problems.
-[1]: http://en.wikipedia.org/wiki/State_(computer_science)#Program_state
-[2]: http://en.wikipedia.org/wiki/Elm_(programming_language)
+[1]: https://en.wikipedia.org/wiki/State_(computer_science)#Program_state
+[2]: https://en.wikipedia.org/wiki/Elm_(programming_language)
diff --git a/2013-02-25-nsassertionhandler.md b/2013-02-25-nsassertionhandler.md
index 3611ec97..2b1d12fc 100644
--- a/2013-02-25-nsassertionhandler.md
+++ b/2013-02-25-nsassertionhandler.md
@@ -1,6 +1,6 @@
---
title: NSAssertionHandler
-author: Mattt Thompson
+author: Mattt
category: Cocoa
excerpt: "Programming incorporates numerous disciplines of human reasoning, from high-level discourse and semantics—the story we tell each other to explain how a system works—to the mathematical and philosophical machinery that underpins everything."
status:
@@ -13,9 +13,9 @@ Programming incorporates numerous disciplines of human reasoning, from high-leve
Assertions are a concept borrowed from classical logic. In logic, assertions are statements about propositions within a proof. In programming, assertions denote assumptions the programmer has made about the application at the place where they are declared.
-When used in the capacity of preconditions and postconditions, which describe expectations about the state of the code at the beginning and end of execution of a method or function, assertions form a [contract](http://en.wikipedia.org/wiki/Design_by_contract). Assertions can also be used to enforce conditions at run-time, in order to prevent execution when certain preconditions fail.
+When used in the capacity of preconditions and postconditions, which describe expectations about the state of the code at the beginning and end of execution of a method or function, assertions form a [contract](https://en.wikipedia.org/wiki/Design_by_contract). Assertions can also be used to enforce conditions at run-time, in order to prevent execution when certain preconditions fail.
-Assertions are similar to [unit testing](http://en.wikipedia.org/wiki/Unit_testing) in that they define expectations about the way code will execute. Unlike unit tests, assertions exist inside the program itself, and are thereby constrained to the context of the program. Because unit tests are fully independent, they have a much greater capacity to isolate and test certain behaviors, using tools like methods stubs and mock objects. Developers should use assertions and unit tests in combination and in reasonable quantity to test and define behavior in an application.
+Assertions are similar to [unit testing](https://en.wikipedia.org/wiki/Unit_testing) in that they define expectations about the way code will execute. Unlike unit tests, assertions exist inside the program itself, and are thereby constrained to the context of the program. Because unit tests are fully independent, they have a much greater capacity to isolate and test certain behaviors, using tools like methods stubs and mock objects. Developers should use assertions and unit tests in combination and in reasonable quantity to test and define behavior in an application.
## Foundation Assertion Handling
@@ -53,14 +53,14 @@ And while Foundation assertion macros are extremely useful in their own right—
### LoggingAssertionHandler.h
-~~~{objective-c}
+```objc
@interface LoggingAssertionHandler : NSAssertionHandler
@end
-~~~
+```
### LoggingAssertionHandler.m
-~~~{objective-c}
+```objc
@implementation LoggingAssertionHandler
- (void)handleFailureInMethod:(SEL)selector
@@ -81,7 +81,7 @@ And while Foundation assertion macros are extremely useful in their own right—
}
@end
-~~~
+```
Each thread has the option of specifying an assertion handler. To have the `NSAssertionHandler` subclass start handling failed assertions, set it as the value for the `NSAssertionHandlerKey` key in the thread's `threadDictionary`.
@@ -90,18 +90,18 @@ didFinishLaunchingWithOptions:`.
### AppDelegate.m
-~~~{objective-c}
+```objc
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSAssertionHandler *assertionHandler = [[LoggingAssertionHandler alloc] init];
[[[NSThread currentThread] threadDictionary] setValue:assertionHandler
forKey:NSAssertionHandlerKey];
- // ...
+ <#...#>
return YES;
}
-~~~
+```
---
diff --git a/2013-03-04-backrow.md b/2013-03-04-backrow.md
index 39d7de1d..fd5db807 100644
--- a/2013-03-04-backrow.md
+++ b/2013-03-04-backrow.md
@@ -1,10 +1,11 @@
---
title: Back Row
-author: Mattt Thompson
-category: ""
+author: Mattt
+category: Miscellaneous
excerpt: "Back Row is a private framework used in the Apple TV user experience. Rather than waiting for Apple to open up the Apple TV SDK, one can take matters into their own hands."
+retired: true
status:
- swift: n/a
+ swift: n/a
---
For years, many have predicted the announcement of an Apple TV SDK. We all know it's coming. It has to. And it will be amazing when it happens... whenever that is.
@@ -40,39 +41,39 @@ Here's a brief list of some Back Row classes & protocols and their UIKit equival
-
<BRResponder>
-
<UIResponder>
+
<BRResponder>
+
<UIResponder>
-
<BRAppliance>
-
<UIApplication>
+
<BRAppliance>
+
<UIApplication>
-
BRController
-
UIViewController
+
BRController
+
UIViewController
-
BRMenuController
-
UITableViewController
+
BRMenuController
+
UITableViewController
-
BRControllerStack
-
UINavigationController
+
BRControllerStack
+
UINavigationController
-
BRGridView
-
UICollectionView
+
BRGridView
+
UICollectionView
-
BRListView
-
UITableView
+
BRListView
+
UITableView
## Apple TV Appliance Structure
-
+
In the current Apple TV interface, the home screen contains a grid of rectangular icons, similar to the home screen on iOS. Each icon corresponds to an _appliance_.
@@ -84,7 +85,7 @@ An appliance also has an optional `topShelfController`, which is what displays a
Pushing and popping controllers is managed by a shared `BRControllerStack`. When a controller is selected with in a list, it is pushed onto the stack. When the user presses the Menu button, the stack is popped.
-
+
A typical controller consists of a menu list on the right, with some kind of complimentary view on the left.
@@ -92,7 +93,7 @@ On top-level controllers, this sometimes takes the form of a `BRMarqueeStack`, w
For controllers listing media that can be played, the complimentary view usually shows a preview image, along with meta data in a `BRMetadataControl`, such as runtime, date created, and other relevant information.
-
+
iTunes store controllers also use a horizontally-stacked layout, with media information at the top, with related titles listed below.
@@ -111,7 +112,7 @@ Here are some of the more interesting parts of the Back Row framework headers (a
## Building Your Own Apple TV App
-In the immortal words of Jeff Goldblum: ["Life finds a way"](http://www.youtube.com/watch?v=SkWeMvrNiOM).
+In the immortal words of Jeff Goldblum: ["Life finds a way"](https://www.youtube.com/watch?v=SkWeMvrNiOM).
Undeterred by the litigious shadow of Apple Inc., nor the undocumented wilds of a private API, a cadre of jail-breaking homesteaders have cracked the nut on Apple TV development with promising results.
@@ -127,4 +128,4 @@ Only time will tell.
In the meantime, you can get a head-start on what will almost certainly be a gold-rush on the scale of the first generation of iPhone apps.
-[1]: http://en.wikipedia.org/wiki/Front_Row_(software)
+[1]: https://en.wikipedia.org/wiki/Front_Row_(software)
diff --git a/2013-03-11-uiappearance.md b/2013-03-11-uiappearance.md
index b95c00dc..272e661f 100644
--- a/2013-03-11-uiappearance.md
+++ b/2013-03-11-uiappearance.md
@@ -1,12 +1,12 @@
---
title: UIAppearance
-author: Mattt Thompson
+author: Mattt
category: Cocoa
tags: nshipster
excerpt: "UIAppearance allows the appearance of views and controls to be consistently defined across the entire application."
status:
- swift: 2.0
- reviewed: September 8, 2015
+ swift: 2.0
+ reviewed: September 8, 2015
---
Style vs. Substance.
@@ -42,7 +42,8 @@ Appearance can be customized for all instances, or scoped to particular view hie
```swift
UINavigationBar.appearance().tintColor = myColor
```
-```objective-c
+
+```objc
[[UINavigationBar appearance] setTintColor:myColor];
```
@@ -58,7 +59,8 @@ UIBarButtonItem.appearanceWhenContainedInInstancesOfClasses([UIToolbar.self])
UIBarButtonItem.appearanceWhenContainedInInstancesOfClasses([UIToolbar.self, UIPopoverController.self])
.tintColor = myNavBarColor
```
-```objective-c
+
+```objc
[[UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[UINavigationBar class]]]
setTintColor:myNavBarColor];
[[UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[UINavigationBar class], [UIPopoverController class]]]
@@ -73,9 +75,9 @@ UIBarButtonItem.appearanceWhenContainedInInstancesOfClasses([UIToolbar.self, UIP
One major downside to `UIAppearance`'s proxy approach is that it's difficult to know which selectors are compatible.
-Because +appearance returns an id, Xcode can't provide any code-completion information. This is a major source of confusion and frustration with this feature.
+Because +appearance returns an id, Xcode can't provide any code-completion information. This is a major source of confusion and frustration with this feature.
-As of iOS 7, UIAppearance now returns instancetype, which allows for code completion to work as expected. Huzzah!
+As of iOS 7, UIAppearance now returns instancetype, which allows for code completion to work as expected. Huzzah!
In order to find out what methods work with `UIAppearance`, you have to [look at the headers](http://stackoverflow.com/questions/9424112/what-properties-can-i-set-via-an-uiappearance-proxy):
@@ -91,21 +93,13 @@ For your convenience, [here is the list of properties as of iOS 7.0](https://gis
## Implementing `` in Custom UIView Subclasses
-Much like how [`NSLocalizedString`](http://nshipster.com/nslocalizedstring/) and [`#pragma`](http://nshipster.com/pragma/) are marks of quality in Objective-C code, having custom UI classes conform to `UIAppearance` is not only a best-practice, but it demonstrates a certain level of care being put into its implementation.
+Much like how [`NSLocalizedString`](https://nshipster.com/nslocalizedstring/) and [`#pragma`](https://nshipster.com/pragma/) are marks of quality in Objective-C code, having custom UI classes conform to `UIAppearance` is not only a best-practice, but it demonstrates a certain level of care being put into its implementation.
[Peter Steinberger](https://twitter.com/steipete) has [this great article](http://petersteinberger.com/blog/2013/uiappearance-for-custom-views/), which describes some of the caveats about implementing `UIAppearance` in custom views. It's a must-read for anyone who aspires to greatness in their open source UI components.
-## Alternatives
-
-Another major shortcoming of `UIAppearance` is that style rules are _imperative_, rather than _declarative_. That is, styling is applied at runtime in code, rather than being interpreted from a list of style rules.
-
-Yes, if there's one idea to steal from web development, it's the separation of content and presentation. Say what you will about CSS, but stylesheets are _amazing_.
-
-Stylesheet enthusiasts on iOS now have some options. [Pixate](http://www.pixate.com) is a commercial framework that uses CSS to style applications. [NUI](https://github.com/tombenner/nui), an open-source project by [Tom Benner](https://github.com/tombenner), does much the same with a CSS/SCSS-like language. Another open source project along the same lines is [UISS](https://github.com/robertwijas/UISS) by [Robert Wijas](https://github.com/robertwijas), which allows `UIAppearance` rules to be read from JSON.
-
---
-Cocoa developers have a long history of obsessing about visual aesthetics, and have often gone to extreme ends to achieve their desired effects. Recall the [Delicious Generation](http://en.wikipedia.org/wiki/Delicious_Generation) of Mac developers, and applications like [Disco](http://discoapp.com), which went so far as to [emit virtual smoke when burning a disc](http://www.youtube.com/watch?v=8Dwi47XOqwI).
+Cocoa developers have a long history of obsessing about visual aesthetics, and have often gone to extreme ends to achieve their desired effects. Recall the [Delicious Generation](https://en.wikipedia.org/wiki/Delicious_Generation) of Mac developers, and applications like [Disco](http://discoapp.com), which went so far as to [emit virtual smoke when burning a disc](https://www.youtube.com/watch?v=8Dwi47XOqwI).
This spirit of dedication to making things look good is alive and well in iOS. As a community and as an ecosystem, we have relentlessly pushed the envelope in terms of what users should expect from their apps. And though this makes our jobs more challenging, it makes the experience of developing for iOS all the more enjoyable.
diff --git a/2013-03-18-c-storage-classes.md b/2013-03-18-c-storage-classes.md
index 402eaf40..6e76389b 100644
--- a/2013-03-18-c-storage-classes.md
+++ b/2013-03-18-c-storage-classes.md
@@ -1,6 +1,6 @@
---
title: C Storage Classes
-author: Mattt Thompson
+author: Mattt
category: Objective-C
tags: nshipster
excerpt: "In C, the scope and lifetime of a variable or function within a program is determined by its storage class. Understanding these storage classes allows us to decipher common incantations found throughout Objective-C"
@@ -33,7 +33,7 @@ Automatic variables have memory automatically allocated when a program enters a
Most Objective-C programmers probably aren't familiar with `register` either, as it's just not widely used in the `NS` world.
-`register` behaves just like `auto`, except that instead of being allocated onto the stack, they are stored in a [register](http://en.wikipedia.org/wiki/Processor_register).
+`register` behaves just like `auto`, except that instead of being allocated onto the stack, they are stored in a [register](https://en.wikipedia.org/wiki/Processor_register).
Registers offer faster access than RAM, but because of the complexities of memory management, putting variables in registers does not guarantee a faster program—in fact, it may very well end up slowing down execution by taking up space on the register unnecessarily. As it were, using `register` is actually just a _suggestion_ to the compiler to store the variable in the register; implementations may choose whether or not to honor this.
@@ -52,7 +52,7 @@ As a keyword, `static` gets used in a lot of different, incompatible ways, so it
A common pattern in Objective-C is the `static` singleton, wherein a statically-declared variable is initialized and returned in either a function or class method. `dispatch once` is used to guarantee that the variable is initialized _exactly_ once in a thread-safe manner:
-~~~{objective-c}
+```objc
+ (instancetype)sharedInstance {
static id _sharedInstance = nil;
static dispatch_once_t onceToken;
@@ -62,7 +62,7 @@ A common pattern in Objective-C is the `static` singleton, wherein a statically-
return _sharedInstance;
}
-~~~
+```
The singleton pattern is useful for creating objects that are shared across the entire application, such as an HTTP client or a notification manager, or objects that may be expensive to create, such as formatters.
@@ -80,15 +80,15 @@ The pattern is to declare an `extern` `NSString * const` in a public header, and
#### AppDelegate.h
-~~~{objective-c}
+```objc
extern NSString * const kAppErrorDomain;
-~~~
+```
#### AppDelegate.m
-~~~{objective-c}
+```objc
NSString * const kAppErrorDomain = @"com.example.yourapp.error";
-~~~
+```
It doesn't particularly matter what the value of the string is, so long as it's unique. Using a string constant establishes a strict contract, that the constant variable is used instead of the string's literal value itself.
@@ -100,7 +100,7 @@ The pattern follows the same as in the previous example:
#### TransactionStateMachine.h
-~~~{objective-c}
+```objc
typedef NS_ENUM(NSUInteger, TransactionState) {
TransactionOpened,
TransactionPending,
@@ -108,11 +108,11 @@ typedef NS_ENUM(NSUInteger, TransactionState) {
};
extern NSString * NSStringFromTransactionState(TransactionState state);
-~~~
+```
#### TransactionStateMachine.m
-~~~{objective-c}
+```objc
NSString * NSStringFromTransactionState(TransactionState state) {
switch (state) {
case TransactionOpened:
@@ -125,7 +125,7 @@ NSString * NSStringFromTransactionState(TransactionState state) {
return nil;
}
}
-~~~
+```
---
diff --git a/2013-03-25-search-kit.md b/2013-03-25-search-kit.md
index e520f9e1..112a89e2 100644
--- a/2013-03-25-search-kit.md
+++ b/2013-03-25-search-kit.md
@@ -1,14 +1,14 @@
---
title: Search Kit
-author: Mattt Thompson
+author: Mattt
category: Cocoa
excerpt: "Search Kit is a C framework for searching and indexing content in human languages. It supports matching on phrase or partial word, including logical & wildcard operators, and can rank results by relevance. Search Kit also provides document summarization, which is useful for generating representative excerpts. And best of all: it's thread-safe."
status:
- swift: 2.0
- reviewed: November 24, 2015
+ swift: 2.0
+ reviewed: November 24, 2015
revisions:
- "2013-03-25": Original publication.
- "2015-11-24": Revised for Swift 2.0.
+ "2013-03-25": Original publication
+ "2015-11-24": Revised for Swift 2.0
---
NSHipsters love irony, right? How about this for irony:
@@ -39,9 +39,9 @@ Finding the answer in a reasonable amount of time requires effort from the start
### Extract
-First, content must be extracted from a [corpus](http://en.wikipedia.org/wiki/Text_corpus). For a text document, this could involve removing any styling, formatting, or other meta-information. For a data record, such as an `NSManagedObject`, this means taking all of the salient fields and combining it into a representation.
+First, content must be extracted from a [corpus](https://en.wikipedia.org/wiki/Text_corpus). For a text document, this could involve removing any styling, formatting, or other meta-information. For a data record, such as an `NSManagedObject`, this means taking all of the salient fields and combining it into a representation.
-Once extracted, the content is [tokenized](http://en.wikipedia.org/wiki/Tokenization) for further processing.
+Once extracted, the content is [tokenized](https://en.wikipedia.org/wiki/Tokenization) for further processing.
### Filter
@@ -49,11 +49,11 @@ In order to get the most relevant matches, it's important to filter out common,
### Reduce
-Along the same lines, words that mean basically the same thing should be reduced down into a common form. Morpheme clusters, such as grammatical conjugations like "computer", "computers", "computed", and "computing", for example, can all be simplified to be just "compute", using a [stemmer](http://en.wikipedia.org/wiki/Stemming). Synonyms, likewise, can be lumped into a common entry using a thesaurus lookup.
+Along the same lines, words that mean basically the same thing should be reduced down into a common form. Morpheme clusters, such as grammatical conjugations like "computer", "computers", "computed", and "computing", for example, can all be simplified to be just "compute", using a [stemmer](https://en.wikipedia.org/wiki/Stemming). Synonyms, likewise, can be lumped into a common entry using a thesaurus lookup.
### Index
-The end result of extracting, filtering, and reducing content into an array of normalized tokens is to form an [inverted index](http://en.wikipedia.org/wiki/Inverted_index), such that each token points to its origin in the index.
+The end result of extracting, filtering, and reducing content into an array of normalized tokens is to form an [inverted index](https://en.wikipedia.org/wiki/Inverted_index), such that each token points to its origin in the index.
After repeating this process for each document or record in the corpus until, each token can point to many different articles. In the process of searching, a query is mapped onto one or many of these tokens, retrieving the union of the articles associated with each token.
@@ -61,19 +61,20 @@ After repeating this process for each document or record in the corpus until, ea
### Creating an Index
-`SKIndexRef` is the central data type in Search Kit, containing all of the information needed to process and fulfill searches, and add information from new documents. Indexes can be persistent / file-based or ephemeral / in-memory. Indexes can either be created from scratch, or loaded from an existing file or data object—and once
+`SKIndexRef` is the central data type in Search Kit, containing all of the information needed to process and fulfill searches, and add information from new documents. Indexes can be persistent / file-based or ephemeral / in-memory. Indexes can either be created from scratch, or loaded from an existing file or data object—and once
an index is finished being used, like many other C APIs, the index is closed.
When starting a new in-memory index, use an empty `NSMutableData` instance as the data store:
-~~~{swift}
+```swift
let mutableData = NSMutableData()
let index = SKIndexCreateWithMutableData(mutableData, nil, SKIndexType(kSKIndexInverted.rawValue), nil).takeRetainedValue()
-~~~
-~~~{objective-c}
+```
+
+```objc
NSMutableData *mutableData = [NSMutableData data];
SKIndexRef index = SKIndexCreateWithMutableData((__bridge CFMutableDataRef)mutableData, NULL, kSKIndexInverted, NULL);
-~~~
+```
### Adding Documents to an Index
@@ -83,53 +84,57 @@ Each `SKDocumentRef` is associated with a URI.
For documents on the file system, the URI is simply the location of the file on disk:
-~~~{swift}
+```swift
let fileURL = NSURL(fileURLWithPath: "/path/to/document")
let document = SKDocumentCreateWithURL(fileURL).takeRetainedValue()
-~~~
-~~~{objective-c}
+```
+
+```objc
NSURL *fileURL = [NSURL fileURLWithPath:@"/path/to/document"];
SKDocumentRef document = SKDocumentCreateWithURL((__bridge CFURLRef)fileURL);
-~~~
+```
For Core Data managed objects, the `NSManagedObjectID -URIRepresentation` can be used:
-~~~{swift}
+```swift
let objectURL = objectID.URIRepresentation()
let document = SKDocumentCreateWithURL(objectURL).takeRetainedValue()
-~~~
-~~~{objective-c}
+```
+
+```objc
NSURL *objectURL = [objectID URIRepresentation];
SKDocumentRef document = SKDocumentCreateWithURL((__bridge CFURLRef)objectURL);
-~~~
+```
> For any other kinds of data, it would be up to the developer to define a URI representation.
When adding the contents of a `SKDocumentRef` to an `SKIndexRef`, the text can either be specified manually:
-~~~{swift}
+```swift
let string = "Lorem ipsum dolar sit amet"
SKIndexAddDocumentWithText(index, document, string, true)
-~~~
-~~~{objective-c}
+```
+
+```objc
NSString *string = @"Lorem ipsum dolar sit amet";
SKIndexAddDocumentWithText(index, document, (__bridge CFStringRef)string, true);
-~~~
+```
...or collected automatically from a file:
-~~~{swift}
+```swift
let mimeTypeHint = "text/rtf"
SKIndexAddDocument(index, document, mimeTypeHint, true)
-~~~
-~~~{objective-c}
+```
+
+```objc
NSString *mimeTypeHint = @"text/rtf";
SKIndexAddDocument(index, document, (__bridge CFStringRef)mimeTypeHint, true);
-~~~
+```
To change the way a file-based document's contents are processed, properties can be defined when creating the index:
-~~~{swift}
+```swift
let stopwords: Set = ["all", "and", "its", "it's", "the"]
let properties: [NSObject: AnyObject] = [
@@ -141,8 +146,9 @@ let properties: [NSObject: AnyObject] = [
]
let index = SKIndexCreateWithURL(url, nil, SKIndexType(kSKIndexInverted.rawValue), properties).takeRetainedValue()
-~~~
-~~~{objective-c}
+```
+
+```objc
NSSet *stopwords = [NSSet setWithObjects:@"all", @"and", @"its", @"it's", @"the", nil];
NSDictionary *properties = @{
@@ -154,25 +160,25 @@ NSDictionary *properties = @{
};
SKIndexRef index = SKIndexCreateWithURL((CFURLRef)url, NULL, kSKIndexInverted, (CFDictionaryRef)properties);
-~~~
+```
After adding to or modifying an index's documents, you'll need to commit the changes to the backing store via `SKIndexFlush()` to make your changes available to a search.
-
### Searching
`SKSearchRef` is the data type constructed to perform a search on an `SKIndexRef`. It contains a reference to the index, a query string, and a set of options:
-~~~{swift}
+```swift
let query = "kind of blue"
let options = SKSearchOptions(kSKSearchOptionDefault)
let search = SKSearchCreate(index, query, options).takeRetainedValue()
-~~~
-~~~{objective-c}
+```
+
+```objc
NSString *query = @"kind of blue";
SKSearchOptions options = kSKSearchOptionDefault;
SKSearchRef search = SKSearchCreate(index, (CFStringRef)query, options);
-~~~
+```
`SKSearchOptions` is a bitmask with the following possible values:
@@ -189,29 +195,30 @@ These options can be specified individually as well:
Just creating an `SKSearchRef` kicks off the asynchronous search; results can be accessed with one or more calls to `SKSearchFindMatches`, which returns a batch of results at a time until you've seen all the matching documents. Iterating through the range of found matches provides access to the document URL and relevance score (if calculated):
-~~~{swift}
+```swift
let limit = ... // Maximum number of results
let time: NSTimeInterval = ... // Maximum time to get results, in seconds
var documentIDs: [SKDocumentID] = Array(count: limit, repeatedValue: 0)
var urls: [Unmanaged?] = Array(count: limit, repeatedValue: nil)
-var scores: [Float] = Array(count: limit, repeatedValue: 0)
+var scores: [Float] = Array(count: limit, repeatedValue: 0)
var foundCount = 0
let hasMoreResults = SKSearchFindMatches(search, limit, &documentIDs, &scores, time, &count)
SKIndexCopyDocumentURLsForDocumentIDs(index, foundCount, &documentIDs, &urls)
-
+
let results: [NSURL] = zip(urls[0 ..< foundCount], scores).flatMap({
(cfurl, score) -> NSURL? in
guard let url = cfurl?.takeRetainedValue() as NSURL?
else { return nil }
-
+
print("- \(url): \(score)")
return url
})
-~~~
-~~~{objective-c}
+```
+
+```objc
NSUInteger limit = ...; // Maximum number of results
NSTimeInterval time = ...; // Maximum time to get results, in seconds
SKDocumentID documentIDs[limit];
@@ -235,7 +242,7 @@ NSMutableArray *mutableResults = [NSMutableArray array];
CFRelease(url);
}];
-~~~
+```
> For more examples of Search Kit in action, be sure to check out [Indragie Karunaratne's](https://github.com/indragiek) project, [SNRSearchIndex](https://github.com/indragiek/SNRSearchIndex).
diff --git a/2013-04-01-icloud.md b/2013-04-01-icloud.md
index 1f249e49..5619700c 100644
--- a/2013-04-01-icloud.md
+++ b/2013-04-01-icloud.md
@@ -1,13 +1,13 @@
---
title: iCloud
-author: Mattt Thompson
-category: ""
+author: Mattt
+category: Miscellaneous
excerpt: "Perhaps what Apple is most renowned for is its consistent track record for creating great webservices. From consumer products like MobileMe and Ping to developer tools including the Provisioning Portal and iTunes Connect, Apple has continued to set new standards for convenience, robustness, and extensibility."
status:
swift: 1.1
---
-The [Lisa](http://en.wikipedia.org/wiki/Apple_Lisa). The [Twentieth Anniversary Macintosh](http://en.wikipedia.org/wiki/Twentieth_Anniversary_Macintosh). The [iPod Hi-Fi](http://en.wikipedia.org/wiki/IPod_Hi-Fi). The [MacBook Wheel](http://www.youtube.com/watch?v=9BnLbv6QYcA).
+The [Lisa](https://en.wikipedia.org/wiki/Apple_Lisa). The [Twentieth Anniversary Macintosh](https://en.wikipedia.org/wiki/Twentieth_Anniversary_Macintosh). The [iPod Hi-Fi](https://en.wikipedia.org/wiki/IPod_Hi-Fi). The [MacBook Wheel](https://www.youtube.com/watch?v=9BnLbv6QYcA).
Each of these products exemplifies Apple's obsessive pursuit of quality as much as its unrivaled ability to anticipate the direction of things to come and execute flawlessly.
@@ -17,7 +17,7 @@ But perhaps what Apple is most renowned for, however, is its consistent track re
So when Apple introduced iCloud at WWDC in 2011, everyone in the audience was rapt in anticipation, overcome with a sense of optimistic wonder. "Not again!", we said to ourselves, shaking our head with a knowing smile. "This changes everything!"
-And indeed, iCloud _has_ changed everything. Although it seemed almost too good to be true when Steve Jobs got up on that stage in Moscone to announce iCloud to the world that morning in June—if anything, Apple has __under__-promised and __over__-delivered with iCloud.
+And indeed, iCloud _has_ changed everything. Although it seemed almost too good to be true when Steve Jobs got up on that stage in Moscone to announce iCloud to the world that morning in June—if anything, Apple has **under**-promised and **over**-delivered with iCloud.
Now, NSHipster usually covers obscure topics that you've probably never heard of. But this is one of those days where it just feels _right_ to take this opportunity today to make sure we're all on the same page about iCloud.
@@ -39,7 +39,7 @@ Everyone knows that key-value storage is the secret to achieving Web Scale. That
Inject a healthy dose of Web Scale convenience into your app by incorporating `NSUbiquitousKeyValueStore` into your app:
-~~~{swift}
+```swift
func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
NSNotificationCenter.defaultCenter().addObserverForName(NSUbiquitousKeyValueStoreDidChangeExternallyNotification, object: NSUbiquitousKeyValueStore.defaultStore(), queue: NSOperationQueue.mainQueue()) { (notification) in
let ubiquitousKeyValueStore = notification.object as NSUbiquitousKeyValueStore
@@ -48,9 +48,9 @@ func application(application: UIApplication!, didFinishLaunchingWithOptions laun
return true
}
-~~~
+```
-~~~{objective-c}
+```objc
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
@@ -69,7 +69,7 @@ didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
NSUbiquitousKeyValueStore *ubiquitousKeyValueStore = notification.object;
[ubiquitousKeyValueStore synchronize];
}
-~~~
+```
## Document Storage... in the Cloud!
diff --git a/2013-04-08-bool.md b/2013-04-08-bool.md
index 14a9b176..c111aab6 100644
--- a/2013-04-08-bool.md
+++ b/2013-04-08-bool.md
@@ -1,110 +1,161 @@
---
title: BOOL / bool / Boolean / NSCFBoolean
-author: Mattt Thompson
+author: Mattt
category: Objective-C
tags: nshipster, popular
-excerpt: "Once again, encoding our logical universe into the cold, calculating bytecode of computers forces us to deal with these questions one way or another. And as you'll see from our discussion of boolean types in Objective-C and its kin, truth is indeed stranger than fiction."
+excerpt: >-
+ Encoding our logical universe into the cold, calculating bytecode of computers
+ forces us to deal with these questions one way or another.
+ And as you'll see from our discussion of Boolean types in Objective-C,
+ truth is indeed stranger than fiction.
status:
- swift: n/a
+ swift: n/a
---
-We've talked before about the [philosophical and technical concerns of nothingness in programming](http://nshipster.com/nil/). This week, our attention turns to another fundamental matter: Truth.
-
-Truth. _Vēritās_. The entire charter of Philosophy is founded upon the pursuit of it, and yet its exact meaning and implications still elude us. Does truth exist independently, or is it defined contingently against falsity? Can a proposition be at once both true _and_ false? Is there absolute truth in anything, or is everything relative?
-
-Once again, encoding our logical universe into the cold, calculating bytecode of computers forces us to deal with these questions one way or another. And as you'll see from our discussion of boolean types in Objective-C and its kin, truth is indeed stranger than fiction.
-
----
-
-Objective-C defines `BOOL` to encode truth value. It is a `typedef` of a `signed char`, with the macros `YES` and `NO` to represent true and false, respectively.
-
-Boolean values are used in conditionals, such as `if` or `while` statements, to conditionally perform logic or repeat execution. When evaluating a conditional statement, the value `0` is considered "false", while any other value is considered "true". Because `NULL` and `nil` are defined as `0`, conditional statements on these nonexistent values are also evaluated as "false".
-
-**In Objective-C, use the `BOOL` type for parameters, properties, and instance variables dealing with truth values. When assigning literal values, use the `YES` and `NO` macros.**
+Truth.
+Vēritās.
+The entire charter of Philosophy is founded upon the pursuit of it,
+and yet its exact meaning and implications still elude us.
+Does truth exist independently or is it defined contingently against falsity?
+Can a proposition be at once both true _and_ false? Is there absolute truth in anything, or is everything relative?
+
+Encoding our logical universe into the cold, calculating bytecode of computers
+forces us to deal with these questions one way or another.
+And, as we'll soon discuss,
+truth is indeed stranger than fiction.
+
+We've talked before about the
+[philosophical and technical concerns of nothingness in programming](/nil/).
+This week,
+our attention turns to another fundamental matter:
+_Boolean types in Objective-C_.
+
+
+* * *
+
+In `C`,
+statements like `if` and `while`
+evaluate a conditional expression to determine
+which code to execute next.
+A value of `0` is considered "false"
+while any other value is considered "true".
+
+Objective-C defines the `BOOL` type to encode truth values.
+Altogether,
+`BOOL` comprises a type definition (`typedef signed char BOOL`)
+and the macros `YES` and `NO`,
+which represent true and false,
+respectively.
+By convention,
+we use the `BOOL` type for Boolean
+parameters,
+properties, and
+instance variables
+and use `YES` and `NO`
+when representing literal Boolean values.
+
+{% info %}
+
+Because `NULL` and `nil` zero values,
+they evaluate to "false" in conditional expressions.
+
+{% endinfo %}
## The Wrong Answer to the Wrong Question
-Novice programmers often include an equality operator when evaluating conditionals:
+Novice programmers often include an equality operator in conditional expressions:
-~~~{objective-c}
+```objc
if ([a isEqual:b] == YES) {
...
}
-~~~
+```
-Not only is this unnecessary, but depending on the left-hand value, it may also cause unexpected results, as described in the [Big Nerd Ranch blog post, "BOOL's Sharp Edges"](http://blog.bignerdranch.com/564-bools-sharp-corners/):
+Not only is this unnecessary,
+but depending on the left-hand value can cause unexpected results,
+as demonstrated by Mark Dalrymple in
+[this blog post](https://www.bignerdranch.com/blog/bools-sharp-corners/)
+with the following example:
-~~~{objective-c}
+```objc
static BOOL different (int a, int b) {
return a - b;
}
-~~~
+```
-An overly clever C programmer might take some satisfaction in the simplicity of this approach: indeed, two integers are equal if and only if their difference is `0`.
+A clever programmer might take some satisfaction in this approach.
+Indeed, two integers are equal if and only if their difference is `0`.
+However,
+because `BOOL` is `typedef`'d as a `signed char`,
+this implementation doesn't behave as expected:
-However, because of the reality of `BOOL` being `typedef`'d as a `signed char`, this will not behave as expected:
-
-~~~{objective-c}
+```objc
if (different(11, 10) == YES) {
printf ("11 != 10\n");
} else {
printf ("11 == 10\n");
}
+// Prints "11 != 10"
if (different(10, 11) == YES) {
printf ("10 != 11\n");
} else {
printf ("10 == 11\n");
}
+// Prints "10 == 11"
if (different(512, 256) == YES) {
printf ("512 != 256\n");
} else {
printf ("512 == 256\n");
}
-~~~
-
-This evaluates to:
+// Prints "512 == 256"
+```
-~~~
-11 != 10
-10 == 11
-512 == 256
-~~~
+[This might be acceptable for JavaScript](https://www.destroyallsoftware.com/talks/wat),
+but Objective-C doesn't suffer fools gladly.
-Now, [this might be acceptable for JavaScript](https://www.destroyallsoftware.com/talks/wat), but Objective-C don't suffer fools gladly.
-
-Deriving truth value directly from an arithmetic operation is never a good idea. Like the sentence ["Colorless green ideas sleep furiously"](http://en.wikipedia.org/wiki/Colorless_green_ideas_sleep_furiously), it may be grammatical (after all, `BOOL` is a `signed char` like any other, so it _could_ be treated as a number), but it doesn't make sense semantically. Instead, use the result of the `==` operator, or cast values into booleans with the `!` (or `!!`) operator.
+Deriving truth value directly from an arithmetic operation is never a good idea.
+Like the sentence
+["Colorless green ideas sleep furiously"](https://en.wikipedia.org/wiki/Colorless_green_ideas_sleep_furiously),
+it may be grammatical but it doesn't make any sense.
+Instead,
+use the result of the `==` operator
+or cast values into booleans with the `!` (or `!!`) operator.
## The Truth About `NSNumber` and `BOOL`
-Pop quiz: what is the output of the following expression?
+**Pop Quiz**: What is the output of the following expression?
-~~~{objective-c}
+```objc
NSLog(@"%@", [@(YES) class]);
-~~~
-
-The answer:
-
- __NSCFBoolean
+```
-Wait, what?
+**Answer**: `__NSCFBoolean`
-All this time, we've been led to believe that `NSNumber` [boxes](http://nshipster.com/nsvalue/) primitives into an object representation. Any other integer- or float-derived `NSNumber` object shows its class to be `__NSCFNumber`. What gives?
+_Wait, what?_
-`NSCFBoolean` is a private class in the `NSNumber` [class cluster](http://nshipster.com/nsorderedset/). It is a bridge to the [`CFBooleanRef` type](https://developer.apple.com/library/mac/#documentation/CoreFoundation/Reference/CFBooleanRef/Reference/reference.html), which is used to wrap boolean values for Core Foundation property lists and collections. `CFBoolean` defines the constants `kCFBooleanTrue` and `kCFBooleanFalse`. Because `CFNumberRef` and `CFBooleanRef` are different types in Core Foundation, it makes sense that they are represented by different bridging classes in `NSNumber`.
+All this time,
+we've been led to believe that `NSNumber`
+[boxes](/nsvalue/) primitives into an object representation.
+Any other integer- or floating-point-derived `NSNumber` object
+shows its class to be `__NSCFNumber`.
+_So what gives?_
-For most people, boolean values and boxed objects "just work", and don't really care what goes into making the sausage. But here at NSHipster, we're all about sausages.
-
----
-
-So, to recap, here is a table of all of the truth types and values in Objective-C:
+`NSCFBoolean` is a private class in the `NSNumber`
+[class cluster](/nsorderedset/).
+It's a bridge to the
+[`CFBooleanRef` type](https://developer.apple.com/library/mac/#documentation/CoreFoundation/Reference/CFBooleanRef/Reference/reference.html),
+which is used to wrap boolean values for Core Foundation property lists and collections.
+`CFBoolean` defines the constants `kCFBooleanTrue` and `kCFBooleanFalse`.
+Because `CFNumberRef` and `CFBooleanRef` are different types in Core Foundation,
+it makes sense that they are represented by different bridging classes in `NSNumber`.
Name
-
Typedef
+
Typedef
Header
True Value
False Value
@@ -112,39 +163,48 @@ So, to recap, here is a table of all of the truth types and values in Objective-
-
BOOL
-
signed char
+
BOOL
+
signed char
objc.h
-
YES
-
NO
+
YES
+
NO
-
bool
-
_Bool (int)
+
bool
+
_Bool (int)
stdbool.h
-
true
-
false
+
true
+
false
-
Boolean
-
unsigned char
+
Boolean
+
unsigned char
MacTypes.h
-
TRUE
-
FALSE
+
TRUE
+
FALSE
-
NSNumber
-
__NSCFBoolean
+
NSNumber
+
__NSCFBoolean
Foundation.h
-
@(YES)
-
@(NO)
+
@(YES)
+
@(NO)
-
CFBooleanRef
-
struct
+
CFBooleanRef
+
struct
CoreFoundation.h
-
kCFBooleanTrue
-
kCFBooleanFalse
+
kCFBooleanTrue
+
kCFBooleanFalse
+
+* * *
+
+For most people,
+Boolean values and boxed objects "just work".
+They don't really care what goes into making the sausage.
+But here at NSHipster, we're all about sausages.
+That's the honest truth.
+
diff --git a/2013-04-15-nssecurecoding.md b/2013-04-15-nssecurecoding.md
index c9b36052..e2f4d8f0 100644
--- a/2013-04-15-nssecurecoding.md
+++ b/2013-04-15-nssecurecoding.md
@@ -1,6 +1,6 @@
---
title: NSSecureCoding
-author: Mattt Thompson
+author: Mattt
category: Cocoa
excerpt: "A short post for this week: everything you need to know about NSSecureCoding."
status:
@@ -23,35 +23,35 @@ Why is this important? Recall that `NSCoding` is Foundation's way of marshaling
It's not an apples-to-apples comparison, but it's somewhat similar to [recent YAML exploit found in Rails](http://tenderlovemaking.com/2013/02/06/yaml-f7u12.html).
-For an [XPC service](http://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingXPCServices.html), which is designed with security in mind, data integrity of this nature is especially important. It's a safe bet that XPC will only wax influence in subsequent iOS and OS X releases, so it's good to keep this all in mind.
+For an [XPC service](https://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingXPCServices.html), which is designed with security in mind, data integrity of this nature is especially important. It's a safe bet that XPC will only wax influence in subsequent iOS and OS X releases, so it's good to keep this all in mind.
Anyway, `NSSecureCoding` patches this vulnerability by establishing a contract for best practices. Now, decoding an object requires the class to be known ahead of time.
Whereas a standard, secure implementation of `-initWithCoder:` might have a check like:
-~~~{swift}
+```swift
if let object = decoder.decodeObjectForKey("key") as? SomeClass {
- // ...
+ <#...#>
}
-~~~
+```
-~~~{objective-c}
+```objc
id obj = [decoder decodeObjectForKey:@"myKey"];
if (![obj isKindOfClass:[MyClass class]]) {
// fail
}
-~~~
+```
...an `NSSecureCoding`-conforming class would use:
-~~~{swift}
+```swift
let object = decoder.decodeObjectOfClass(SomeClass.self, forKey: "key") as SomeClass
-~~~
+```
-~~~{objective-c}
+```objc
id obj = [decoder decodeObjectOfClass:[MyClass class]
forKey:@"myKey"];
-~~~
+```
Sometimes, a little API change makes all of the difference.
diff --git a/2013-04-22-nshipster-quiz-1.md b/2013-04-22-nshipster-quiz-1.md
index a97825b0..94e49f02 100644
--- a/2013-04-22-nshipster-quiz-1.md
+++ b/2013-04-22-nshipster-quiz-1.md
@@ -1,6 +1,6 @@
---
title: "NSHipster Quiz #1"
-author: Mattt Thompson
+author: Mattt
category: Trivia
excerpt: "Test your knowledge of general programming knowledge, Cocoa APIs, and Apple trivia in this first-ever NSHipster Quiz. How NSHip are you?"
status:
@@ -60,43 +60,43 @@ Round 3: Picture Round
- 1. What is this?
-
+
- 2. What is this?
-
+
- 3. What is this?
-
+
- 4. What is this?
-
+
- 5. WTF is this?
-
+
- 6. Who is this?
-
+
- 7. Who is this?
-
+
- 8. Who is this?
-
+
- 9. Who is this?
-
+
- 10. In this photo, Bill Gates & Steve Jobs are being interviewed at the D5 conference in 2007 by a man and a woman just off-screen to the left. Who are they? (One point for each person)
-
+
Round 4: Name That Framework!
@@ -123,16 +123,16 @@ For each question, a list of three classes from the same framework have been lis
Round 1: General Knowledge
--------------------------
-1. [NeXTSTEP](http://en.wikipedia.org/wiki/NeXTSTEP)
+1. [NeXTSTEP](https://en.wikipedia.org/wiki/NeXTSTEP)
2. [4000](http://www.macrumors.com/2013/03/04/steve-jobs-4000-latte-prank-order-lives-on-at-san-francisco-starbucks/)
-3. [`isReady`, `isExecuting`, `isFinished`, `isCancelled`](http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/NSOperation_class/Reference/Reference.html%23//apple_ref/doc/uid/TP40004591-RH2-DontLinkElementID_1)
-4. [ textLabel detailTextLabel ](http://developer.apple.com/library/ios/DOCUMENTATION/UserExperience/Conceptual/TableView_iPhone/Art/tvcellstyle_value2.jpg)
-5. [`UITableViewDelegate`](http://developer.apple.com/library/ios/documentation/uikit/reference/UITableViewDelegate_Protocol/Reference/Reference.html#//apple_ref/doc/uid/TP40006942-CH3-SW25)
-6. [`signed char`](http://nshipster.com/bool/)
-7. [Midnight UTC, 1 January 1970](http://en.wikipedia.org/wiki/Unix_epoch)
-8. [4.6.2 (4H1003)](http://en.wikipedia.org/wiki/Xcode)
-9. [NSIndexSet](http://nshipster.com/nsindexset/)
-10. [16](http://en.wikipedia.org/wiki/IPhone_%281st_generation%29)
+3. [`isReady`, `isExecuting`, `isFinished`, `isCancelled`](https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/NSOperation_class/Reference/Reference.html%23//apple_ref/doc/uid/TP40004591-RH2-DontLinkElementID_1)
+4. [ textLabel detailTextLabel ](https://developer.apple.com/library/ios/DOCUMENTATION/UserExperience/Conceptual/TableView_iPhone/Art/tvcellstyle_value2.jpg)
+5. [`UITableViewDelegate`](https://developer.apple.com/library/ios/documentation/uikit/reference/UITableViewDelegate_Protocol/Reference/Reference.html#//apple_ref/doc/uid/TP40006942-CH3-SW25)
+6. [`signed char`](https://nshipster.com/bool/)
+7. [Midnight UTC, 1 January 1970](https://en.wikipedia.org/wiki/Unix_epoch)
+8. [4.6.2 (4H1003)](https://en.wikipedia.org/wiki/Xcode)
+9. [NSIndexSet](https://nshipster.com/nsindexset/)
+10. [16](https://en.wikipedia.org/wiki/IPhone_%281st_generation%29)
Round 2: APIs
-------------
@@ -151,29 +151,29 @@ Round 2: APIs
Round 3: Picture Round
----------------------
-1. [Apple I](http://en.wikipedia.org/wiki/Apple_I)
-2. [Apple eMac](http://en.wikipedia.org/wiki/EMac)
-3. [Apple Bandai Pippin](http://en.wikipedia.org/wiki/Apple_Bandai_Pippin)
-4. [Apple QuickTake](http://en.wikipedia.org/wiki/Apple_QuickTake)
+1. [Apple I](https://en.wikipedia.org/wiki/Apple_I)
+2. [Apple eMac](https://en.wikipedia.org/wiki/EMac)
+3. [Apple Bandai Pippin](https://en.wikipedia.org/wiki/Apple_Bandai_Pippin)
+4. [Apple QuickTake](https://en.wikipedia.org/wiki/Apple_QuickTake)
5. [New Proposed Apple Campus / "Mothership"](http://www.cultofmac.com/108782/apples-magnificent-mothership-campus-gets-new-renders-and-more-details-report/)
-6. [Sir Jonathan "Jony" Ive](http://en.wikipedia.org/wiki/Jonathan_Ive)
-7. [Scott Forstall](http://en.wikipedia.org/wiki/Scott_Forstall)
-8. [Bob Mansfield](http://en.wikipedia.org/wiki/Bob_Mansfield)
-9. [Susan Kare](http://en.wikipedia.org/wiki/Susan_kare)
+6. [Sir Jonathan "Jony" Ive](https://en.wikipedia.org/wiki/Jonathan_Ive)
+7. [Scott Forstall](https://en.wikipedia.org/wiki/Scott_Forstall)
+8. [Bob Mansfield](https://en.wikipedia.org/wiki/Bob_Mansfield)
+9. [Susan Kare](https://en.wikipedia.org/wiki/Susan_kare)
10. [Kara Swisher & Walt Mossberg ](http://allthingsd.com/20071224/best-of-2007-video-d5-interview-with-bill-gates-and-steve-jobs/)
Round 4: Name That Framework!
-----------------------------
-1. [App Kit](http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/ObjC_classic/_index.html)
+1. [App Kit](https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/ObjC_classic/_index.html)
2. [AV Foundation](https://developer.apple.com/library/mac/#documentation/AVFoundation/Reference/AVFoundationFramework/_index.html)
-3. [Publication Subscription](http://developer.apple.com/library/mac/#documentation/InternetWeb/Reference/PubSubReference/_index.html#//apple_ref/doc/uid/TP40004649)
-4. [Core Location](http://developer.apple.com/library/ios/#documentation/CoreLocation/Reference/CoreLocation_Framework/_index.html)
-5. [Core Data](http://developer.apple.com/library/ios/#documentation/cocoa/Reference/CoreData_ObjC/_index.html)
+3. [Publication Subscription](https://developer.apple.com/library/mac/#documentation/InternetWeb/Reference/PubSubReference/_index.html#//apple_ref/doc/uid/TP40004649)
+4. [Core Location](https://developer.apple.com/library/ios/#documentation/CoreLocation/Reference/CoreLocation_Framework/_index.html)
+5. [Core Data](https://developer.apple.com/library/ios/#documentation/cocoa/Reference/CoreData_ObjC/_index.html)
6. [Search Kit](https://developer.apple.com/library/mac/#documentation/UserExperience/Reference/SearchKit/Reference/reference.html)
-7. [Address Book](http://developer.apple.com/library/ios/#documentation/AddressBook/Reference/AddressBook_iPhoneOS_Framework/_index.html)
-8. [GLKit](http://developer.apple.com/library/mac/#documentation/GLkit/Reference/GLKit_Collection/_index.html)
-9. [Core Bluetooth](http://developer.apple.com/library/ios/#documentation/CoreBluetooth/Reference/CoreBluetooth_Framework/_index.html)
+7. [Address Book](https://developer.apple.com/library/ios/#documentation/AddressBook/Reference/AddressBook_iPhoneOS_Framework/_index.html)
+8. [GLKit](https://developer.apple.com/library/mac/#documentation/GLkit/Reference/GLKit_Collection/_index.html)
+9. [Core Bluetooth](https://developer.apple.com/library/ios/#documentation/CoreBluetooth/Reference/CoreBluetooth_Framework/_index.html)
10. [Core Image](https://developer.apple.com/library/mac/#documentation/graphicsimaging/Conceptual/CoreImaging/ci_intro/ci_intro.html)
* * *
diff --git a/2013-04-29-mklocalsearch.md b/2013-04-29-mklocalsearch.md
index 75b28061..f3f324a3 100644
--- a/2013-04-29-mklocalsearch.md
+++ b/2013-04-29-mklocalsearch.md
@@ -1,6 +1,6 @@
---
title: MKLocalSearch
-author: Mattt Thompson
+author: Mattt
category: Cocoa
excerpt: "In all of the hubbub of torch burning and pitchfork raising, you may have completely missed a slew of additions to MapKit in iOS 6.1."
status:
@@ -20,7 +20,7 @@ In all of the hubbub of torch burning and pitchfork raising, you may have comple
But before you go and rush into using `MKLocalSearch`, you'll have to know a few things about its friends. You see, `MKLocalSearch` has its functionality divided across `MKLocalSearchRequest` and `MKLocalSearchResponse`:
-~~~{swift}
+```swift
let request = MKLocalSearchRequest()
request.naturalLanguageQuery = "Restaurants"
request.region = mapView.region
@@ -33,11 +33,11 @@ search.startWithCompletionHandler { (response, error) in
}
for item in response.mapItems {
- // ...
+ <#...#>
}
}
-~~~
-~~~{objective-c}
+```
+```objc
MKLocalSearchRequest *request = [[MKLocalSearchRequest alloc] init];
request.naturalLanguageQuery = @"Restaurants";
request.region = mapView.region;
@@ -45,7 +45,7 @@ MKLocalSearch *search = [[MKLocalSearch alloc] initWithRequest:request];
[search startWithCompletionHandler:^(MKLocalSearchResponse *response, NSError *error) {
NSLog(@"Map Items: %@", response.mapItems);
}];
-~~~
+```
`MKLocalSearchRequest` takes a `naturalLanguageQuery`, such as "Taxidermists", and an optional bounding geographic `region` to constrain results. In practice, the `region` is usually passed from an `MKMapView`.
diff --git a/2013-05-06-gpuimage.md b/2013-05-06-gpuimage.md
index f0f88917..e38b0843 100644
--- a/2013-05-06-gpuimage.md
+++ b/2013-05-06-gpuimage.md
@@ -1,13 +1,14 @@
---
title: GPUImage
-author: Mattt Thompson
+author: Mattt
category: Open Source
excerpt: "GPUImage is a BSD-licensed iOS library that lets you apply GPU-accelerated filters and other effects to images, live camera video, and movies. If you're not careful, you may well end up creating a camera app by the end of the article."
+retired: true
status:
- swift: n/a
+ swift: n/a
---
-Here at NSHipster, we're all about diving into the darker corners of Objective-C to learn something new about the systems we interact with every day. Often, this means sifting through Apple frameworks or language features (it is, after all, a lot of what it means to work in Objective-C). However, [on occasion](http://nshipster.com/reactivecocoa/), it's nice to take a look to the burgeoning landscape of third-party libraries and frameworks (and there are some truly remarkable ones) for a glimpse of what's new and great outside of Cupertino.
+Here at NSHipster, we're all about diving into the darker corners of Objective-C to learn something new about the systems we interact with every day. Often, this means sifting through Apple frameworks or language features (it is, after all, a lot of what it means to work in Objective-C). However, [on occasion](https://nshipster.com/reactivecocoa/), it's nice to take a look to the burgeoning landscape of third-party libraries and frameworks (and there are some truly remarkable ones) for a glimpse of what's new and great outside of Cupertino.
This week, we'll be taking a look at one of the most impressive open source projects you'll find: [GPUImage](https://github.com/BradLarson/GPUImage). Buckle up, NSHipsters—if you're not careful, you may well end up creating a camera app by the end of the article.
@@ -17,11 +18,11 @@ GPUImage is a BSD-licensed iOS library written by [Brad Larson](https://github.c
## GPU vs. CPU
-Every iPhone ships with two processors: a [CPU](http://en.wikipedia.org/wiki/Central_processing_unit), or Central Processing Unit and a [GPU](http://en.wikipedia.org/wiki/Graphics_processing_unit), or Graphics Processing Unit. Each processor has its own strengths, and modern chip architecture (like in the iPhone's [A4](http://en.wikipedia.org/wiki/Apple_A4)) integrate the CPU and GPU onto the same physical die.
+Every iPhone ships with two processors: a [CPU](https://en.wikipedia.org/wiki/Central_processing_unit), or Central Processing Unit and a [GPU](https://en.wikipedia.org/wiki/Graphics_processing_unit), or Graphics Processing Unit. Each processor has its own strengths, and modern chip architecture (like in the iPhone's [A4](https://en.wikipedia.org/wiki/Apple_A4)) integrate the CPU and GPU onto the same physical die.
-When you write C or Objective-C code in Xcode, you're generating instructions that will be handled almost exclusively by the CPU. The GPU, by contrast, is a specialized chip that is especially well-suited for computation that can be split out into many small, independent operations, such as graphics rendering. The kinds of instructions understood by the GPU are quite different from that of the CPU, and as such, we write this code in a different language: [OpenGL](http://en.wikipedia.org/wiki/Opengl) (or specifically, [OpenGL ES](http://en.wikipedia.org/wiki/OpenGL_ES) on the iPhone & iPad).
+When you write C or Objective-C code in Xcode, you're generating instructions that will be handled almost exclusively by the CPU. The GPU, by contrast, is a specialized chip that is especially well-suited for computation that can be split out into many small, independent operations, such as graphics rendering. The kinds of instructions understood by the GPU are quite different from that of the CPU, and as such, we write this code in a different language: [OpenGL](https://en.wikipedia.org/wiki/Opengl) (or specifically, [OpenGL ES](https://en.wikipedia.org/wiki/OpenGL_ES) on the iPhone & iPad).
-> Check out [Jeff LaMarche's GLProgram OpenGL ES 2.0 book](http://iphonedevelopment.blogspot.com/2010/11/opengl-es-20-for-ios-chapter-4.html) for a great introduction to OpenGL ES and the rendering pipeline.
+> Check out [Jeff LaMarche's GLProgram OpenGL ES 2.0 book](https://web.archive.org/web/20150909104553/http://iphonedevelopment.blogspot.in:80/2010/11/opengl-es-20-for-ios-chapter-4.html) for a great introduction to OpenGL ES and the rendering pipeline.
Comparing the performance of GPU-based rendering to CPU rendering for something like video, the differences are staggering:
@@ -223,18 +224,17 @@ Here's a table of the 125 (!) filters that come with GPUImage:
-Seriously, the [Filter Showcase Example App](https://github.com/BradLarson/GPUImage/tree/master/examples/iOS/FilterShowcase) that comes bundled in the repository could easily retail on the AppStore for $3.99, as-is. Add Twitter integration and a few sound effects, and you could bump that up to a respectable $6.99.
+Seriously, the [Filter Showcase Example App](https://github.com/BradLarson/GPUImage/tree/master/examples/iOS/FilterShowcase) that comes bundled in the repository could easily retail on the AppStore for $3.99, as-is. Add Twitter integration and a few sound effects, and you could bump that up to a respectable$6.99.
## Rendering Pipeline
-
-
+{% asset gpuimage-pipeline.svg %}
GPUImage is, at its core, an Objective-C abstraction around a rendering pipeline. Source images from the camera, network, or disk are loaded and manipulated according to a chain of filters, and finally outputted either a view, graphics context, or data stream.
-For example, images from the video camera could have a Color Levels filter applied to simulate different types of [color blindness](http://en.wikipedia.org/wiki/Color_blindness) and displayed in a live view.
+For example, images from the video camera could have a Color Levels filter applied to simulate different types of [color blindness](https://en.wikipedia.org/wiki/Color_blindness) and displayed in a live view.
-~~~{objective-c}
+```objc
GPUImageVideoCamera *videoCamera = [[GPUImageVideoCamera alloc]
initWithSessionPreset:AVCaptureSessionPreset640x480
cameraPosition:AVCaptureDevicePositionBack];
@@ -251,11 +251,11 @@ GPUImageView *filteredVideoView = [[GPUImageView alloc] initWithFrame:self.view.
[self.view addSubview:filteredVideoView];
[videoCamera startCameraCapture];
-~~~
+```
Or, combining various color blending modes, image effects, and adjustments, you could transform still images into something worthy of sharing with your hipster friends (example taken from [FilterKit](https://github.com/eklipse2k8/FilterKit/blob/master/FilterKit/FilterKit/Filters/FKBlueValentine.m), which is built on GPUImage):
-~~~{objective-c}
+```objc
GPUImageFilterGroup *filter = [[GPUImageFilterGroup alloc] init];
GPUImageSaturationFilter *saturationFilter = [[GPUImageSaturationFilter alloc] init];
@@ -275,11 +275,10 @@ GPUImageExposureFilter *exposureFilter = [[GPUImageExposureFilter alloc] init];
[filter addGPUFilter:monochromeFilter];
[filter addGPUFilter:saturationFilter];
[filter addGPUFilter:vignetteFilter];
-~~~
+```
---
Looking through all of what GPUImage can do, one can't help but get _excited_. Easy enough to get started immediately (without needing to know anything about OpenGL) yet performant enough to power whatever you dream up. And not just that, but it also comes with a dizzying number of building blocks—all of the color adjustments, blending modes, and visual effects you could ever want (or never knew you needed).
GPUImage is a rare treat for the open source community, and we as Mac & iOS developers are lucky to have it at our disposal. Use it to make something great, and show others the world in a whole new way.
-
diff --git a/2013-05-13-nscoding.md b/2013-05-13-nscoding.md
index 2fcf65e3..08a5e2fe 100644
--- a/2013-05-13-nscoding.md
+++ b/2013-05-13-nscoding.md
@@ -1,6 +1,6 @@
---
title: NSCoding / NSKeyedArchiver
-author: Mattt Thompson
+author: Mattt
category: Cocoa
excerpt: "Among the most important architectural decisions made when building an app is how to persist data between launches. The question of how, exactly, to re-create the state of the app from the time it was last opened; of how to describe the object graph in such a way that it can be flawlessly reconstructed next time."
status:
@@ -96,7 +96,7 @@ This article will look at the how's, when's, and why's of `NSKeyedArchiver` and
For example:
-~~~{swift}
+```swift
class Book: NSObject, NSCoding {
var title: String
var author: String
@@ -138,9 +138,9 @@ class Book: NSObject, NSCoding {
coder.encodeBool(self.available, forKey: "available")
}
}
-~~~
+```
-~~~{objective-c}
+```objc
@interface Book : NSObject
@property NSString *title;
@property NSString *author;
@@ -177,7 +177,7 @@ class Book: NSObject, NSCoding {
}
@end
-~~~
+```
As you can see, `NSCoding` is mostly boilerplate. Each property is encoded or decoded as an object or type, using the name of the property of as the key each time. (Some developers prefer to define `NSString *` constants for each keypath, but this is usually unnecessary).
@@ -200,23 +200,23 @@ An `NSCoding`-backed table view controller might, for instance, set its collecti
#### Archiving
-~~~{swift}
+```swift
NSKeyedArchiver.archiveRootObject(books, toFile: "/path/to/archive")
-~~~
+```
-~~~{objective-c}
+```objc
[NSKeyedArchiver archiveRootObject:books toFile:@"/path/to/archive"];
-~~~
+```
#### Unarchiving
-~~~{swift}
+```swift
guard let books = NSKeyedUnarchiver.unarchiveObjectWithFile("/path/to/archive") as? [Book] else { return nil }
-~~~
+```
-~~~{objective-c}
+```objc
[NSKeyedUnarchiver unarchiveObjectWithFile:@"/path/to/archive"];
-~~~
+```
## `NSUserDefaults`
@@ -226,28 +226,28 @@ While it is not advisable to store an entire object graph into `NSUserDefaults`,
#### Archiving
-~~~{swift}
+```swift
let data = NSKeyedArchiver.archivedDataWithRootObject(books)
NSUserDefaults.standardUserDefaults().setObject(data, forKey: "books")
-~~~
+```
-~~~{objective-c}
+```objc
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:books];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:@"books"];
-~~~
+```
#### Unarchiving
-~~~{swift}
+```swift
if let data = NSUserDefaults.standardUserDefaults().objectForKey("books") as? NSData {
let books = NSKeyedUnarchiver.unarchiveObjectWithData(data)
}
-~~~
+```
-~~~{objective-c}
+```objc
NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:@"books"];
NSArray *books = [NSKeyedUnarchiver unarchiveObjectWithData:data];
-~~~
+```
---
@@ -258,6 +258,6 @@ The decision to use Core Data in an application may appear to be a no-brainer, i
And even if most applications _would_ benefit from Core Data at some point, there is wisdom to letting complexity evolve from a simple as necessary. And as far as persistence goes, it doesn't get much simpler than `NSCoding`.
-[1]: http://developer.apple.com/library/mac/#documentation/cocoa/Conceptual/CoreData/cdProgrammingGuide.html
-[2]: http://developer.apple.com/library/ios/#Documentation/Cocoa/Reference/Foundation/Classes/NSKeyedArchiver_Class/Reference/Reference.html
-[3]: http://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSKeyedUnarchiver_Class/Reference/Reference.html
+[1]: https://developer.apple.com/library/mac/#documentation/cocoa/Conceptual/CoreData/cdProgrammingGuide.html
+[2]: https://developer.apple.com/library/ios/#Documentation/Cocoa/Reference/Foundation/Classes/NSKeyedArchiver_Class/Reference/Reference.html
+[3]: https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSKeyedUnarchiver_Class/Reference/Reference.html
diff --git a/2013-05-20-core-data-libraries-and-utilities.md b/2013-05-20-core-data-libraries-and-utilities.md
index 79b3d61c..07d2124c 100644
--- a/2013-05-20-core-data-libraries-and-utilities.md
+++ b/2013-05-20-core-data-libraries-and-utilities.md
@@ -1,18 +1,19 @@
---
title: "Core Data Libraries & Utilities"
-author: Mattt Thompson
+author: Mattt
category: Open Source
excerpt: "We were a bit hard on Core Data last week, so for this issue of NSHipster, we bring you a guided tour of the best open source libraries for working with Core Data. Read on to see how you might make the most from your Core Data experience."
+retired: true
status:
- swift: n/a
- reviewed: August 12, 2015
+ swift: n/a
+ reviewed: August 12, 2015
---
-So let's say that, having determined your particular needs and compared all of the alternatives, you've chosen [Core Data](http://developer.apple.com/library/mac/#documentation/cocoa/Conceptual/CoreData/cdProgrammingGuide.html) for your next app.
+So let's say that, having determined your particular needs and compared all of the alternatives, you've chosen [Core Data](https://developer.apple.com/library/mac/#documentation/cocoa/Conceptual/CoreData/cdProgrammingGuide.html) for your next app.
Nothing wrong with that! Core Data is a great choice for apps that model, persist, and query large object graphs.
-Sure it's complicated, cumbersome, and yes, at times, a real [pain in the ass](http://nshipster.com/nscoding#figure-2)—but gosh darn it, some of the best and most popular apps ever built use Core Data. And if it's good enough for them, it's probably good enough for you, too.
+Sure it's complicated, cumbersome, and yes, at times, a real [pain in the ass](https://nshipster.com/nscoding#figure-2)—but gosh darn it, some of the best and most popular apps ever built use Core Data. And if it's good enough for them, it's probably good enough for you, too.
...but that's not to say that Core Data can't be improved.
@@ -34,27 +35,22 @@ This week on NSHipster: a guided tour of the best open source libraries for work
@@ -145,7 +127,7 @@ There are a number of open source libraries that collectively identify and corre
It should be no surprise that programmers, having learned how to do things a certain way, will bring those ideas and conventions to other technologies. For the large influx of Ruby developers coming over to iOS, that familiar paradigm was [Active Record](http://api.rubyonrails.org/classes/ActiveRecord/Base.html).
-Contrary to popular belief, Core Data is _not_ an [Object-Relational Mapper](http://en.wikipedia.org/wiki/Object-relational_mapping), but rather an object graph and persistence framework, capable of much more than the [Active Record pattern](http://en.wikipedia.org/wiki/Active_record_pattern) alone is capable of. Using Core Data as an ORM necessarily limits the capabilities of Core Data and muddies its conceptual purity. But for many developers longing for the familiarity of an ORM, this trade-off is a deal at twice the price!
+Contrary to popular belief, Core Data is _not_ an [Object-Relational Mapper](https://en.wikipedia.org/wiki/Object-relational_mapping), but rather an object graph and persistence framework, capable of much more than the [Active Record pattern](https://en.wikipedia.org/wiki/Active_record_pattern) alone is capable of. Using Core Data as an ORM necessarily limits the capabilities of Core Data and muddies its conceptual purity. But for many developers longing for the familiarity of an ORM, this trade-off is a deal at twice the price!
#### [Magical Record](https://github.com/magicalpanda/MagicalRecord)
@@ -158,17 +140,17 @@ Contrary to popular belief, Core Data is _not_ an [Object-Relational Mapper](htt
> And yeah, no AppDelegate code.
> It's fully tested with [Kiwi](https://github.com/kiwi-bdd/Kiwi).
-### Inspired by [LINQ](http://en.wikipedia.org/wiki/Language_Integrated_Query)
+### Inspired by [LINQ](https://en.wikipedia.org/wiki/Language_Integrated_Query)
-Here's a fun game: the next time you meet a developer coming over from the .NET world, set a timer to see how long it takes them to start raving about [LINQ](http://en.wikipedia.org/wiki/Language_Integrated_Query). Seriously, people _love_ LINQ.
+Here's a fun game: the next time you meet a developer coming over from the .NET world, set a timer to see how long it takes them to start raving about [LINQ](https://en.wikipedia.org/wiki/Language_Integrated_Query). Seriously, people _love_ LINQ.
-For the uninitiated, LINQ is like [SQL](http://en.wikipedia.org/wiki/SQL), but integrated as a language feature. Think `NSPredicate`, [`NSSortDescriptor`](http://nshipster.com/nssortdescriptor/), and [`Key-Value Coding`](http://nshipster.com/kvc-collection-operators/) with a much nicer syntax:
+For the uninitiated, LINQ is like [SQL](https://en.wikipedia.org/wiki/SQL), but integrated as a language feature. Think `NSPredicate`, [`NSSortDescriptor`](https://nshipster.com/nssortdescriptor/), and [`Key-Value Coding`](https://nshipster.com/kvc-collection-operators/) with a much nicer syntax:
-~~~
+```
from c in SomeCollection
where c.SomeProperty < 10
select new {c.SomeProperty, c.OtherProperty};
-~~~
+```
#### [ios-queryable](https://github.com/martydill/ios-queryable)
@@ -176,7 +158,7 @@ from c in SomeCollection
### Inspired by [ReactiveCocoa](https://github.com/ReactiveCocoa)
-ReactiveCocoa, which itself [brings the functional reactive paradigm to Objective-C](http://nshipster.com/reactivecocoa/), is now being used to bring some functional sanity and order to Core Data. This is still uncharted territory, but the initial results are indeed promising.
+ReactiveCocoa, which itself [brings the functional reactive paradigm to Objective-C](https://nshipster.com/reactivecocoa/), is now being used to bring some functional sanity and order to Core Data. This is still uncharted territory, but the initial results are indeed promising.
#### [ReactiveCoreData](https://github.com/apparentsoft/ReactiveCoreData)
@@ -192,7 +174,7 @@ Fortunately, there are a wealth of open-source libraries that can help alleviate
#### [RestKit](https://github.com/RestKit/RestKit)
-> RestKit is a modern Objective-C framework for implementing RESTful web services clients on iOS and OS X. It provides a powerful [object mapping](https://github.com/RestKit/RestKit/wiki/Object-mapping) engine that seamlessly integrates with [Core Data](http://developer.apple.com/library/mac/#documentation/cocoa/Conceptual/CoreData/cdProgrammingGuide.html) and a simple set of networking primitives for mapping HTTP requests and responses built on top of [AFNetworking](https://github.com/AFNetworking/AFNetworking). It has an elegant, carefully designed set of APIs that make accessing and modeling RESTful resources feel almost magical.
+> RestKit is a modern Objective-C framework for implementing RESTful web services clients on iOS and OS X. It provides a powerful [object mapping](https://github.com/RestKit/RestKit/wiki/Object-mapping) engine that seamlessly integrates with [Core Data](https://developer.apple.com/library/mac/#documentation/cocoa/Conceptual/CoreData/cdProgrammingGuide.html) and a simple set of networking primitives for mapping HTTP requests and responses built on top of [AFNetworking](https://github.com/AFNetworking/AFNetworking). It has an elegant, carefully designed set of APIs that make accessing and modeling RESTful resources feel almost magical.
#### [AFIncrementalStore](https://github.com/AFNetworking/AFIncrementalStore)
@@ -232,11 +214,10 @@ We would be remiss to survey the open source Core Data ecosystem without mention
#### [Mogenerator](https://github.com/rentzsch/mogenerator)
-> `mogenerator` is a command-line tool that, given an `.xcdatamodel` file, will generate *two classes per entity*. The first class, `_MyEntity`, is intended solely for machine consumption and will be continuously overwritten to stay in sync with your data model. The second class, `MyEntity`, subclasses `_MyEntity`, won't ever be overwritten and is a great place to put your custom logic.
+> `mogenerator` is a command-line tool that, given an `.xcdatamodel` file, will generate _two classes per entity_. The first class, `_MyEntity`, is intended solely for machine consumption and will be continuously overwritten to stay in sync with your data model. The second class, `MyEntity`, subclasses `_MyEntity`, won't ever be overwritten and is a great place to put your custom logic.
---
Remember: there is no silver bullet. There is no one-size-fits-all solution. Just as Core Data may only be advisable in particular circumstances, so too are the aforementioned Core Data libraries.
Dividing the ecosystem up into broad categories is informative if only to help identify the relative strengths and trade-offs of each library. Only you can determine (yes, sometimes through trial and error) which solution is the best for you.
-
diff --git a/2013-05-27-unit-testing.md b/2013-05-27-unit-testing.md
index 606d6dfe..ab9fc26a 100644
--- a/2013-05-27-unit-testing.md
+++ b/2013-05-27-unit-testing.md
@@ -1,6 +1,6 @@
---
title: Unit Testing
-author: Mattt Thompson
+author: Mattt
category: Objective-C
excerpt: "Unit Testing is an emotional topic for developers. It inspires a sense of superiority to its most zealous adherents, and evokes a feeling of inadequacy to non-practitioners. Cargo Cults like TDD stake their reputation on unit testing to the point of co-opting and conflating utility with morality."
status:
@@ -29,7 +29,7 @@ It's a simple enough premise: write code to construct environments that exercise
Unit Tests were added into a separate testing target in the Xcode Project. Each test file defines an `SenTestCase` subclass, which implements a series of methods beginning with the word `test`. C `assert`-style macros are used to fail tests if the specified condition is not met. Each test is run in sequence, independently of one another, with the results logged afterwards:
-~~~{objective-c}
+```objc
#import
#import "Person.h"
@@ -43,7 +43,7 @@ Unit Tests were added into a separate testing target in the Xcode Project. Each
person.lastName = @"Picasso";
STAssertEqualObjects([person fullName], @"Pablo Picasso", nil);
}
-~~~
+```
The SenTestingKit assertions are about what you'd expect, offering bread-and-butter equality, existence, and truth checks:
@@ -70,7 +70,7 @@ The only chance testing has to remain relevant in high-pressure situations is to
## Open Source Libraries
-There are a myriad of open source libraries that attempt to make testing more palatable by way of syntactic sugar and features like [method stubs](https://en.wikipedia.org/wiki/Method_stub), [mock objects](https://en.wikipedia.org/wiki/Mock_object), and [promises](http://en.wikipedia.org/wiki/Futures_and_promises).
+There are a myriad of open source libraries that attempt to make testing more palatable by way of syntactic sugar and features like [method stubs](https://en.wikipedia.org/wiki/Method_stub), [mock objects](https://en.wikipedia.org/wiki/Mock_object), and [promises](https://en.wikipedia.org/wiki/Futures_and_promises).
Here's a list of some of the most useful open source libraries for unit testing:
@@ -82,12 +82,10 @@ Here's a list of some of the most useful open source libraries for unit testing:
@@ -144,7 +136,6 @@ Here's a list of some of the most useful open source libraries for unit testing:
Making tests easier to write is one thing, but getting them to run without affecting productivity is quite another.
-
### Jenkins
For a long time, installing [Jenkins](http://jenkins-ci.org) on a dedicated Mac Mini was the state-of-the-art for automated build servers.
@@ -165,7 +156,7 @@ All of the configuration for setup is defined in `.travis.yml`:
#### .travis.yml
-~~~
+```
language: objective-c
before_install:
- brew update
@@ -173,7 +164,7 @@ before_install:
- cd Tests && pod install && cd $TRAVIS_BUILD_DIR
- mkdir -p "Tests/AFNetworking Tests.xcodeproj/xcshareddata/xcschemes" && cp Tests/Schemes/*.xcscheme "Tests/AFNetworking Tests.xcodeproj/xcshareddata/xcschemes/"
script: rake test
-~~~
+```
Full documentation for the Travis configuration file [can be found on Travis-CI.org](http://about.travis-ci.org/docs/user/build-configuration/).
diff --git a/2013-06-02-nsdatadetector.md b/2013-06-02-nsdatadetector.md
index b9bb87ab..e5e87e8a 100644
--- a/2013-06-02-nsdatadetector.md
+++ b/2013-06-02-nsdatadetector.md
@@ -1,61 +1,140 @@
---
title: NSDataDetector
-author: Mattt Thompson
+author: Mattt
category: Cocoa
tags: nshipster
-excerpt: "Until humanity embraces RDF for all of their daily interactions, a large chunk of artificial intelligence is going to go into figuring out what the heck we're all talking about. Fortunately for Cocoa developers, there's NSDataDetector."
+excerpt: >-
+ Until humanity embraces RDF for our daily interactions,
+ computers will have to work overtime
+ to figure out what the heck we're all talking about.
+revisions:
+ "2013-06-02": Original publication
+ "2018-08-29": Updated for Swift 4.2
status:
- swift: 2.0
- reviewed: September 8, 2015
+ swift: 4.2
+ reviewed: August 29, 2018
---
-Machines speak in binary, while humans speak in riddles, half-truths, and omissions.
+Text means nothing without context.
-And until humanity embraces [RDF](http://en.wikipedia.org/wiki/Resource_Description_Framework) for all of their daily interactions, a large chunk of artificial intelligence is going to go into figuring out what the heck we're all talking about.
+What gives weight to our words
+is their relation to one another,
+to ourselves,
+and to our location space-time.
-Because in the basic interactions of our daily lives—meeting people, making plans, finding information online—there is immense value in automatically converting from implicit human language to explicit structured data, so that it can be easily added to our calendars, address books, maps, and reminders.
+Consider
+endophoric expressions
+whose meaning depends on the surrounding text,
+or deictic expressions,
+whose meaning is dependent on who the speaker is,
+where they are, and when they said it.
+Now consider how difficult it would be
+for a computer to make sense of an utterance like
+_"I'll be home in 5 minutes"_?
+(And that's to say nothing of the challenges of
+ambiguity and variation
+in representations of dates, addresses, and other information.)
-Fortunately for Cocoa developers, there's an easy solution: `NSDataDetector`.
+For better or worse,
+that's how we communicate.
+And until humanity embraces
+[RDF](https://www.w3.org/RDF/)
+for our daily interactions,
+computers will have to work overtime
+to figure out what the heck we're all talking about.
---
-`NSDataDetector` is a subclass of [`NSRegularExpression`](https://developer.apple.com/library/mac/#documentation/Foundation/Reference/NSRegularExpression_Class/Reference/Reference.html), but instead of matching on an ICU pattern, it detects semi-structured information: dates, addresses, links, phone numbers and transit information.
+There's immense value in transforming natural language
+into structured data that's compatible with our
+calendars, address books, maps, and reminders.
+Manual data entry, however, amounts to drudgery,
+and is the last thing you want to force on users.
-It does all of this with frightening accuracy. `NSDataDetector` will match flight numbers, address snippets, oddly formatted digits, and even relative deictic expressions like "next Saturday at 5".
+On other platforms,
+you might delegate this task to a web service
+or hack something together that works well enough.
+Fortunately for us Cocoa developers,
+Foundation us covered with `NSDataDetector`.
-You can think of it as a regexp matcher with incredibly complicated expressions that can extract information from natural language (though its actual implementation details may be somewhat more complicated than that).
+You can use `NSDataDetector` to extract
+dates, links, phone numbers, addresses, and transit information
+from natural language text.
-`NSDataDetector` objects are initialized with a bitmask of types of information to check, and then passed strings to match on. Like `NSRegularExpression`, each match found in a string is represented by a `NSTextCheckingResult`, which has details like character range and match type. However, `NSDataDetector`-specific types may also contain metadata such as address or date components.
+First, create a detector,
+by specifying the result types that you're interested in.
+Then call the `enumerateMatches(in:options:range:using:)` method,
+passing the text to be processed.
+The provided closure is executed once for each result.
-~~~{swift}
-let string = "123 Main St. / (555) 555-5555"
-let types: NSTextCheckingType = [.Address, .PhoneNumber]
-let detector = try? NSDataDetector(types: types.rawValue)
-detector?.enumerateMatchesInString(string, options: [], range: NSMakeRange(0, (string as NSString).length)) { (result, flags, _) in
+```swift
+let string = "123 Main St. / (555) 555-1234"
+
+let types: NSTextCheckingResult.CheckingType = [.phoneNumber, .address]
+let detector = try NSDataDetector(types: types.rawValue)
+detector.enumerateMatches(in: string,
+ options: [],
+ range: range) { (result, _, _) in
print(result)
}
-~~~
-~~~{objective-c}
+```
+
+```objc
+NSString *string = @"123 Main St. / (555) 555-1234";
+
NSError *error = nil;
-NSDataDetector *detector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeAddress
- | NSTextCheckingTypePhoneNumber
- error:&error];
+NSDataDetector *detector =
+ [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeAddress |
+ NSTextCheckingTypePhoneNumber
+ error:&error];
-NSString *string = @"123 Main St. / (555) 555-5555";
[detector enumerateMatchesInString:string
options:kNilOptions
range:NSMakeRange(0, [string length])
usingBlock:
^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
- NSLog(@"Match: %@", result);
+ NSLog(@"%@", result);
}];
-~~~
+```
+
+As you might expect,
+running this code produces two results:
+the address "123 Main St."
+and the phone number "(555) 555-1234".
+
+> When initializing `NSDataDetector`,
+> specify only the types you're interested in
+> because any unused types will only slow you down.
+
+## Discerning Information from Results
+
+`NSDataDetector` produces `NSTextCheckingResult` objects.
+
+On the one hand,
+this makes sense
+because `NSDataDetector` is actually a subclass of `NSRegularExpression`.
+On the other hand,
+there's not much overlap between a pattern match and detected data
+other than the range and type.
+So what you get is an API that's polluted
+and offers no strong guarantees about what information is present
+under which circumstances.
-> When initializing `NSDataDetector`, be sure to specify only the types you're interested in. With each additional type to be checked comes a nontrivial performance cost.
+> To make matters worse,
+> `NSTextCheckingResult` is also used by `NSSpellServer`.
+> _Gross._
-## Data Detector Match Types
+To get information about data detector results,
+you need to first check its `resultType`;
+depending on that,
+you might access information directly through properties,
+(in the case of links, phone numbers, and dates),
+or indirectly by keyed values on the `components` property
+(for addresses and transit information).
-Because of how much `NSTextCheckingResult` is used for, it's not immediately clear which properties are specific to `NSDataDetector`. For your reference, here is a table of the different `NSTextCheckingTypes` for `NSDataDetector` matches, and their associated properties:
+Here's a rundown of the various
+`NSDataDetector` result types
+and their associated properties:
@@ -65,125 +144,202 @@ Because of how much `NSTextCheckingResult` is used for, it's not immediately cle
-
-
NSTextCheckingTypeDate
+
.link
-
date
-
duration
-
timeZone
+
.url
-
NSTextCheckingTypeAddress
+
.phoneNumber
-
addressComponents*
-
-
NSTextCheckingNameKey
-
NSTextCheckingJobTitleKey
-
NSTextCheckingOrganizationKey
-
NSTextCheckingStreetKey
-
NSTextCheckingCityKey
-
NSTextCheckingStateKey
-
NSTextCheckingZIPKey
-
NSTextCheckingCountryKey
-
NSTextCheckingPhoneKey
-
+
.phoneNumber
-
NSTextCheckingTypeLink
+
.date
-
url
+
.date
+
.duration
+
.timeZone
-
NSTextCheckingTypePhoneNumber
+
.address
-
phoneNumber
+
.components
+
+
.name
+
.jobTitle
+
.organization
+
.street
+
.city
+
.state
+
.zip
+
.country
+
.phone
+
-
NSTextCheckingTypeTransitInformation
+
.transitInformation
-
components*
+
.components
-
NSTextCheckingAirlineKey
-
NSTextCheckingFlightKey
+
.airline
+
.flight
-
-
-
*NSDictionary properties have values at defined keys.
-
-## Data Detection on iOS
+## Data Detector Data Points
-Somewhat confusingly, iOS also defines `UIDataDetectorTypes`. A bitmask of these values can be set as the `dataDetectorTypes` of a `UITextView` to have detected data automatically linked in the displayed text.
+Let's put `NSDataDetector` through its paces.
+That way, we'll not only have a complete example of how to use it
+to its full capacity
+but see what it's actually capable of.
-`UIDataDetectorTypes` is distinct from `NSTextCheckingTypes` in that equivalent enum constants (e.g. `UIDataDetectorTypePhoneNumber` and `NSTextCheckingTypePhoneNumber`) do not have the same integer value, and not all values in one are found in the other. Converting from `UIDataDetectorTypes` to `NSTextCheckingTypes` can be accomplished with a function:
+The following text contains one of each of the type of data
+that `NSDataDetector` should be able to detect:
-~~~{swift}
-func NSTextCheckingTypesFromUIDataDetectorTypes(dataDetectorType: UIDataDetectorTypes) -> NSTextCheckingType {
- var textCheckingType: NSTextCheckingType = []
-
- if dataDetectorType.contains(.Address) {
- textCheckingType.insert(.Address)
- }
-
- if dataDetectorType.contains(.CalendarEvent) {
- textCheckingType.insert(.Date)
- }
-
- if dataDetectorType.contains(.Link) {
- textCheckingType.insert(.Link)
+```swift
+let string = """
+ My flight (AA10) is scheduled for tomorrow night from 9 PM PST to 5 AM EST.
+ I'll be staying at The Plaza Hotel, 768 5th Ave, New York, NY 10019.
+ You can reach me at 555-555-1234 or me@example.com
+"""
+```
+
+We can have `NSDataDetector` check for everything
+by passing `NSTextCheckingAllTypes` to its initializer.
+The rest is a matter of switching over each `resultType`
+and extracting their respective details:
+
+```swift
+let detector = try NSDataDetector(types: NSTextCheckingAllTypes)
+let range = NSRange(string.startIndex.. Even after trying a handful of different representations
+> ("American Airlines 10", "AA 10", "AA #10", "American Airlines (AA) #10")
+> and airlines
+> ("Delta 1226", "DL 1226")
+> I still wasn't able to find an example that populated the `airline` property.
+> If anyone knows what's up, [@ us](https://twitter.com/NSHipster/).
+
+## Detect (Rough) Edges
+
+Useful as `NSDataDetector` is,
+it's not a particularly _nice_ API to use.
+
+With all of the charms of its parent class,
+[`NSRegularExpression`](https://nshipster.com/nsregularexpression/),
+the same, cumbersome initialization pattern of
+[NSLinguisticTagger](https://nshipster.com/nslinguistictagger/),
+and an
+[incomplete Swift interface](https://developer.apple.com/documentation/foundation/nstextcheckingtypes),
+`NSDataDetector` has an interface that only a mother could love.
+
+But that's only the API itself.
+
+In a broader context,
+you might be surprised to learn that a nearly identical API can be found
+in the `dataDetectorTypes` properties of `UITextView` and `WKWebView`.
+_Nearly_ identical.
+
+`UIDataDetectorTypes` and `WKDataDetectorTypes` are distinct from
+and incompatible with `NSTextCheckingTypes`,
+which is inconvenient but not super conspicuous.
+But what's utterly inexplicable is that these APIs
+can detect [shipment tracking numbers](https://developer.apple.com/documentation/uikit/uidatadetectortypes/1648142-shipmenttrackingnumber)
+and [lookup suggestions](https://developer.apple.com/documentation/uikit/uidatadetectortypes/1648141-lookupsuggestion),
+neither of which are supported by `NSDataDetector`.
+It's hard to imagine why shipment tracking numbers wouldn't be supported,
+which leads one to believe that it's an oversight.
---
-Do I detect some disbelief of how easy it is to translate between natural language and structured data? This should not be surprising, given how [insanely](http://nshipster.com/cfstringtransform/) [great](http://nshipster.com/nslinguistictagger/) Cocoa's linguistic APIs are.
+Humans have an innate ability to derive meaning from language.
+We can stitch together linguistic, situational and cultural information
+into a coherent interpretation at a subconscious level.
+Ironically, it's difficult to put this process into words ---
+or code as the case may be.
+Computers aren't hard-wired for understanding like we are.
-Don't make your users re-enter information by hand just because of a programming oversight. Take advantage of `NSDataDetector` in your app to unlock the structured information already hiding in plain sight.
+Despite its shortcomings,
+`NSDataDetector` can prove invaluable for certain use cases.
+Until something better comes along,
+take advantage of it in your app
+to unlock the structured information hiding in plain sight.
diff --git a/2013-06-12-nshipster-quiz-2.md b/2013-06-12-nshipster-quiz-2.md
index ea986289..94c3efc5 100644
--- a/2013-06-12-nshipster-quiz-2.md
+++ b/2013-06-12-nshipster-quiz-2.md
@@ -1,13 +1,13 @@
---
title: "NSHipster Quiz #2"
-author: Mattt Thompson
+author: Mattt
category: Trivia
excerpt: "Go up against some of the brightest minds in all things Apple at this special WWDC edition of NSHipster Quiz. Sure, you conform to the NSHipster protocol, but do you have what it takes to implement all of the @optional methods? Take the quiz and see for yourself!"
status:
swift: n/a
---
-On June 11th, we organized an NSHipster Pub Quiz for WWDC attendees. Like [our first quiz](http://nshipster.com/nshipster-quiz-1/), questions ranged from random Apple trivia to obscure framework questions. The event was hosted by [New Relic](http://newrelic.com), and sponsored by [Heroku](https://heroku.com) & [Mutual Mobile](http://www.mutualmobile.com). About 100 developers attended the event, with the team "UIResponders" taking top prize.
+On June 11th, we organized an NSHipster Pub Quiz for WWDC attendees. Like [our first quiz](https://nshipster.com/nshipster-quiz-1/), questions ranged from random Apple trivia to obscure framework questions. The event was hosted by [New Relic](http://newrelic.com), and sponsored by [Heroku](https://heroku.com) & [Mutual Mobile](http://www.mutualmobile.com). About 100 developers attended the event, with the team "UIResponders" taking top prize.
For everyone that couldn't make it to the event, here's an opportunity to play along at home. Here are some ground rules:
@@ -62,43 +62,43 @@ Round 3: Picture Round
- 1. Which WWDC keynote was this from?
-
+
- 2. Which WWDC keynote was this from?
-
+
- 3. Which WWDC keynote was this from?
-
+
- 4. Which WWDC keynote was this from?
-
+
- 5. WTF is this?
-
+
- 6. What is this?
-
+
- 7. What is this?
-
+
- 8. What is this? (and which generation?)
-
+
- 9. Which "Core" framework is represented by this logo?
-
+
- 10. Everybody loves Craig _/fɛdɹ̩igi/_ (Pictured). How do you spell his last name?
-
+
Round 4: Name That Framework!
-----------------------------
@@ -123,13 +123,13 @@ For each question, a list of three classes from the same framework have been lis
Round 1: General Knowledge
--------------------------
-1. [A white-haired German Shepherd named Maverick](http://en.wikipedia.org/wiki/Mavericks_(location)#History) (anything about a dog gets the point)
-2. [`^@`](http://nshipster.com/type-encodings/)
+1. [A white-haired German Shepherd named Maverick](https://en.wikipedia.org/wiki/Mavericks_(location)#History) (anything about a dog gets the point)
+2. [`^@`](https://nshipster.com/type-encodings/)
3. [`genstrings`](https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/genstrings.1.html)
4. `NSArray -firstObject`
5. [ICU (International Components for Unicode)](http://site.icu-project.org)
-6. [ Tysons Corner, Virginia & Glendale, California](http://en.wikipedia.org/wiki/Apple_Store#History)
-7. [BackRow](http://nshipster.com/backrow/)
+6. [ Tysons Corner, Virginia & Glendale, California](https://en.wikipedia.org/wiki/Apple_Store#History)
+7. [BackRow](https://nshipster.com/backrow/)
8. [Phil Schiller](http://www.theverge.com/2013/6/13/4423844/cant-innovate-anymore-my-ass-apple)
9. Pacific Heights, Mission, Nob Hill, Russian Hill, Marina, Presidio
10. ["So let's raise the bar / And our cups to the stars"](http://rock.rapgenius.com/Daft-punk-get-lucky-lyrics)
@@ -138,12 +138,12 @@ Round 1: General Knowledge
Round 2: Before & After
-----------------------
-1. [Cocoa](http://en.wikipedia.org/wiki/Cocoa_%28API%29)
-2. [Sherlock](http://en.wikipedia.org/wiki/Sherlock_%28Software%29)
-3. [Mac OS X 10.2](http://en.wikipedia.org/wiki/Mac_OS_X_v10.2)
-4. [Bonjour](http://en.wikipedia.org/wiki/Bonjour_%28Software%29)
-5. [iTunes](http://en.wikipedia.org/wiki/Itunes#History)
-6. [System 6 / Mac OS 6](http://en.wikipedia.org/wiki/System_6)
+1. [Cocoa](https://en.wikipedia.org/wiki/Cocoa_%28API%29)
+2. [Sherlock](https://en.wikipedia.org/wiki/Sherlock_%28Software%29)
+3. [Mac OS X 10.2](https://en.wikipedia.org/wiki/Mac_OS_X_v10.2)
+4. [Bonjour](https://en.wikipedia.org/wiki/Bonjour_%28Software%29)
+5. [iTunes](https://en.wikipedia.org/wiki/Itunes#History)
+6. [System 6 / Mac OS 6](https://en.wikipedia.org/wiki/System_6)
7. [Accessibility](http://www.apple.com/accessibility/)
8. [Pixar](https://en.wikipedia.org/wiki/Pixar)
9. [1 Infinite Loop](https://en.wikipedia.org/wiki/1_infinite_loop)
@@ -156,25 +156,25 @@ Round 3: Picture Round
2. 2009
3. 2012
4. 2008
-5. [eMate 300](http://en.wikipedia.org/wiki/EMate_300)
-6. [Xserve RAID](http://en.wikipedia.org/wiki/Xserve_RAID)
-7. [iSight](http://en.wikipedia.org/wiki/ISight)
-8. [3rd gen. iPod Shuffle](http://en.wikipedia.org/wiki/Ipod_shuffle#Third_generation)
-9. [Core Audio](http://en.wikipedia.org/wiki/Core_Audio)
-10. ["Federighi"](http://en.wikipedia.org/wiki/Craig_Federighi)
+5. [eMate 300](https://en.wikipedia.org/wiki/EMate_300)
+6. [Xserve RAID](https://en.wikipedia.org/wiki/Xserve_RAID)
+7. [iSight](https://en.wikipedia.org/wiki/ISight)
+8. [3rd gen. iPod Shuffle](https://en.wikipedia.org/wiki/Ipod_shuffle#Third_generation)
+9. [Core Audio](https://en.wikipedia.org/wiki/Core_Audio)
+10. ["Federighi"](https://en.wikipedia.org/wiki/Craig_Federighi)
Round 4: Name That Framework!
-----------------------------
-1. [Core Telephony](http://developer.apple.com/library/ios/#documentation/NetworkingInternet/Reference/CoreTelephonyFrameworkReference/_index.html)
-2. [Core Motion](http://developer.apple.com/library/ios/#documentation/CoreMotion/Reference/CoreMotion_Reference/_index.html)
-3. [Ad Support](http://developer.apple.com/library/ios/#documentation/DeviceInformation/Reference/AdSupport_Framework/_index.html)
-4. [Social](http://developer.apple.com/library/ios/#documentation/Social/Reference/Social_Framework/_index.html%23//apple_ref/doc/uid/TP40012233)
+1. [Core Telephony](https://developer.apple.com/library/ios/#documentation/NetworkingInternet/Reference/CoreTelephonyFrameworkReference/_index.html)
+2. [Core Motion](https://developer.apple.com/library/ios/#documentation/CoreMotion/Reference/CoreMotion_Reference/_index.html)
+3. [Ad Support](https://developer.apple.com/library/ios/#documentation/DeviceInformation/Reference/AdSupport_Framework/_index.html)
+4. [Social](https://developer.apple.com/library/ios/#documentation/Social/Reference/Social_Framework/_index.html%23//apple_ref/doc/uid/TP40012233)
5. [Foundation](https://developer.apple.com/library/mac/#documentation/cocoa/reference/foundation/ObjC_classic/_index.html)
-6. [EventKit](http://developer.apple.com/library/ios/#documentation/EventKit/Reference/EventKitFrameworkRef/_index.html)
-7. [Game Kit](http://developer.apple.com/library/ios/#documentation/GameKit/Reference/GameKit_Collection/_index.html)
+6. [EventKit](https://developer.apple.com/library/ios/#documentation/EventKit/Reference/EventKitFrameworkRef/_index.html)
+7. [Game Kit](https://developer.apple.com/library/ios/#documentation/GameKit/Reference/GameKit_Collection/_index.html)
8. [Foundation](https://developer.apple.com/library/mac/#documentation/cocoa/reference/foundation/ObjC_classic/_index.html)
-9. [Core Data](http://developer.apple.com/library/ios/#documentation/Cocoa/Reference/CoreData_ObjC/_index.html)
+9. [Core Data](https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/CoreData_ObjC/_index.html)
10. [Foundation](https://developer.apple.com/library/mac/#documentation/cocoa/reference/foundation/ObjC_classic/_index.html) or [Core Foundation](https://developer.apple.com/library/mac/#documentation/CoreFoundation/Reference/CoreFoundation_Collection/_index.html) (2 points if you got both)
* * *
diff --git a/2013-06-17-object-subscripting.md b/2013-06-17-object-subscripting.md
index 82ca3a2b..0417ddfd 100644
--- a/2013-06-17-object-subscripting.md
+++ b/2013-06-17-object-subscripting.md
@@ -1,6 +1,6 @@
---
title: Object Subscripting
-author: Mattt Thompson
+author: Mattt
category: Objective-C
excerpt: "Xcode 4.4 quietly introduced a syntactic revolution to Objective-C. Like all revolutions, however, its origins and agitators require some effort to trace."
status:
@@ -19,17 +19,17 @@ Clang 3.1 added three features to Objective-C whose aesthetic & cosmetic impact
In a single Xcode release, Objective-C went from this:
-~~~{objective-c}
+```objc
NSDictionary *dictionary = [NSDictionary dictionaryWithObject:[NSNumber numberWithInteger:42] forKey:@"foo"];
id value = [dictionary objectForKey:@"foo"];
-~~~
+```
...to this:
-~~~{objective-c}
+```objc
NSDictionary *dictionary = @{@"foo": @42};
id value = dictionary[@"foo"];
-~~~
+```
Concision is the essence of clarity.
@@ -49,10 +49,10 @@ Over time, scripting languages began to take greater liberties with this familia
With Clang 3.1, everything has come full-circle: what began as a C operator and co-opted by scripting languages, has now been rolled back into Objective-C. And like the aforementioned scripting languages of yore, the `[]` subscripting operator in Objective-C has been similarly overloaded to handle both integer-indexed and object-keyed accessors.
-~~~{objective-c}
+```objc
dictionary[@"foo"] = @42;
array[0] = @"bar"
-~~~
+```
> If Objective-C is a superset of C, how can Object Subscripting overload the `[]` C operator? The modern Objective-C runtime prohibits pointer arithmetic on objects, making this semantic pivot possible.
@@ -62,10 +62,10 @@ Where this really becomes interesting is when you extend your own classes with s
To add custom-indexed subscripting support to your class, simply declare and implement the following methods:
-~~~{objective-c}
+```objc
- (id)objectAtIndexedSubscript:(*IndexType*)idx;
- (void)setObject:(id)obj atIndexedSubscript:(*IndexType*)idx;
-~~~
+```
`*IndexType*` can be any integral type, such as `char`, `int`, or `NSUInteger`, as used by `NSArray`.
@@ -73,10 +73,10 @@ To add custom-indexed subscripting support to your class, simply declare and imp
Similarly, custom-keyed subscripting can be added to your class by declaring and implementing these methods:
-~~~{objective-c}
+```objc
- (id)objectForKeyedSubscript:(*KeyType*)key;
- (void)setObject:(id)obj forKeyedSubscript:(*KeyType*)key;
-~~~
+```
`*KeyType*` can be any Objective-C object pointer type.
@@ -86,25 +86,25 @@ Similarly, custom-keyed subscripting can be added to your class by declaring and
The whole point in describing all of this is to encourage unconventional thinking about this whole language feature. At the moment, a majority of custom subscripting in classes is used as a convenience accessor to a private collection class. But there's nothing to stop you from, for instance, doing this:
-~~~{objective-c}
+```objc
routes[@"GET /users/:id"] = ^(NSNumber *userID){
- // ...
+ <#...#>
}
-~~~
+```
...or this:
-~~~{objective-c}
+```objc
id piece = chessBoard[@"E1"];
-~~~
+```
...or this:
-~~~{objective-c}
+```objc
NSArray *results = managedObjectContext[@"Product WHERE stock > 20"];
-~~~
+```
-Because of how flexible and concise subscripting is, it is extremely well-purposed for creating [DSL](http://en.wikipedia.org/wiki/Domain-specific_language)s. When defining custom subscripting methods on your own class, there are no restrictions on how they are implemented. You can use this syntax to provide a shorthand for defining application routes, search queries, compound property accessors, or plain-old KVO.
+Because of how flexible and concise subscripting is, it is extremely well-purposed for creating [DSL](https://en.wikipedia.org/wiki/Domain-specific_language)s. When defining custom subscripting methods on your own class, there are no restrictions on how they are implemented. You can use this syntax to provide a shorthand for defining application routes, search queries, compound property accessors, or plain-old KVO.
---
diff --git a/2013-06-24-uuid-udid-unique-identifier.md b/2013-06-24-uuid-udid-unique-identifier.md
index 6ad4ae9e..51fcfa47 100644
--- a/2013-06-24-uuid-udid-unique-identifier.md
+++ b/2013-06-24-uuid-udid-unique-identifier.md
@@ -1,6 +1,6 @@
---
title: "NSUUID / CFUUIDRef / UIDevice -uniqueIdentifier / -identifierForVendor"
-author: Mattt Thompson
+author: Mattt
category: Cocoa
excerpt: "Until recently, it was trivial to uniquely identify devices between application launches, and even across applications: a simple call to UIDevice -uniqueIdentifier, and you were all set."
status:
@@ -21,7 +21,7 @@ Just as privacy and piracy have phonetic and conceptual similarities, device ide
- **UUID _(Universally Unique Identifier)_**: A sequence of 128 bits that can guarantee uniqueness across space and time, defined by [RFC 4122](http://www.ietf.org/rfc/rfc4122.txt).
- **GUID _(Globally Unique Identifier)_**: Microsoft's implementation of the UUID specification; often used interchangeably with UUID.
-- **UDID _(Unique Device Identifier)_**: A sequence of 40 hexadecimal characters that uniquely identify an iOS device (the device's [Social Security Number](https://en.wikipedia.org/wiki/Social_Security_number), if you will). This value can be [retrieved through iTunes](http://whatsmyudid.com), or found using `UIDevice -uniqueIdentifier`. Derived from hardware details like [MAC address](http://en.wikipedia.org/wiki/MAC_address).
+- **UDID _(Unique Device Identifier)_**: A sequence of 40 hexadecimal characters that uniquely identify an iOS device (the device's [Social Security Number](https://en.wikipedia.org/wiki/Social_Security_number), if you will). This value can be [retrieved through iTunes](http://whatsmyudid.com), or found using `UIDevice -uniqueIdentifier`. Derived from hardware details like [MAC address](https://en.wikipedia.org/wiki/MAC_address).
Incidentally, all of the suggested replacements for `UIDevice -uniqueIdentifier` listed in its deprecation notes return UUID, whether created automatically with `UIDevice -identifierForVendor` & `ASIdentifierManager -advertisingIdentifier` or manually with `NSUUID` (or `CFUUIDCreate`).
@@ -41,38 +41,38 @@ However, for advertising networks, which require a consistent identifier across
> iOS 6 introduces the Advertising Identifier, a non-permanent, non-personal, device identifier, that advertising networks will use to give you more control over advertisers’ ability to use tracking methods. If you choose to limit ad tracking, advertising networks using the Advertising Identifier may no longer gather information to serve you targeted ads. In the future all advertising networks will be required to use the Advertising Identifier. However, until advertising networks transition to using the Advertising Identifier you may still receive targeted ads from other networks.
-As the sole component of the [Ad Support framework](http://developer.apple.com/library/ios/#documentation/DeviceInformation/Reference/AdSupport_Framework/_index.html#//apple_ref/doc/uid/TP40012658), `ASIdentifierManager`'s modus operandi is clear: provide a way for ad networks to track users between different applications in a way that doesn't compromise privacy.
+As the sole component of the [Ad Support framework](https://developer.apple.com/library/ios/#documentation/DeviceInformation/Reference/AdSupport_Framework/_index.html#//apple_ref/doc/uid/TP40012658), `ASIdentifierManager`'s modus operandi is clear: provide a way for ad networks to track users between different applications in a way that doesn't compromise privacy.
Users can opt out of ad targeting in a Settings screen added in iOS 6.1, found at **Settings > General > About > Advertising**:
-
+
## NSUUID & CFUUIDRef
`NSUUID` was added to Foundation in iOS 6 as a way to easily create UUIDs. How easy?
-~~~{swift}
-let UUID = NSUUID().UUIDString
-~~~
+```swift
+let UUID = NSUUID.UUID().UUIDString
+```
-~~~{objective-c}
+```objc
NSString *UUID = [[NSUUID UUID] UUIDString];
-~~~
+```
If your app targets iOS 5 or earlier, however, you have to settle for Core Foundation functions on `CFUUIDRef`:
-~~~{swift}
+```swift
let UUID = CFUUIDCreateString(nil, CFUUIDCreate(nil))
-~~~
+```
-~~~{objective-c}
+```objc
CFUUIDRef uuid = CFUUIDCreate(NULL);
NSString *UUID = CFUUIDCreateString(NULL, uuid);
-~~~
+```
-For apps building against a base SDK without the vendor or advertising identifier APIs, a similar effect can be achieved—as recommended in the deprecation notes—by using [`NSUserDefaults`](http://developer.apple.com/library/ios/#documentation/cocoa/reference/foundation/Classes/NSUserDefaults_Class/Reference/Reference.html):
+For apps building against a base SDK without the vendor or advertising identifier APIs, a similar effect can be achieved—as recommended in the deprecation notes—by using [`NSUserDefaults`](https://developer.apple.com/library/ios/#documentation/cocoa/reference/foundation/Classes/NSUserDefaults_Class/Reference/Reference.html):
-~~~{swift}
+```swift
func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
let userDefaults = NSUserDefaults.standardUserDefaults()
@@ -85,9 +85,9 @@ For apps building against a base SDK without the vendor or advertising identifie
return true
}
-~~~
+```
-~~~{objective-c}
+```objc
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
@@ -101,7 +101,7 @@ didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
-~~~
+```
This way, a UUID will be generated once when the app is launched for the first time, and then stored in `NSUserDefaults` to be retrieved on each subsequent app launch. Unlike advertising or vendor identifiers, these identifiers would not be shared across other apps, but for most intents and purposes, this is works just fine.
diff --git a/2013-07-01-enumerators.md b/2013-07-01-enumerators.md
index 3f7bcd1f..dc499725 100644
--- a/2013-07-01-enumerators.md
+++ b/2013-07-01-enumerators.md
@@ -1,6 +1,6 @@
---
title: "NSFastEnumeration / NSEnumerator"
-author: Mattt Thompson
+author: Mattt
category: Cocoa
excerpt: "Enumeration is where computation gets interesting. It's one thing to encode logic that's executed once, but applying it across a collection—that's what makes programming so powerful."
status:
@@ -25,26 +25,26 @@ This article will cover all of the different ways collections are enumerated in
`for` and `while` loops are the "classic" method of iterating over a collection. Anyone who's taken Computer Science 101 has written code like this before:
-~~~{objective-c}
+```objc
for (NSUInteger i = 0; i < [array count]; i++) {
id object = array[i];
NSLog(@"%@", object)
}
-~~~
+```
-But as anyone who has used C-style loops knows, this method is prone to [off-by-one errors](http://en.wikipedia.org/wiki/Off-by-one_error)—particularly when used in a non-standard way.
+But as anyone who has used C-style loops knows, this method is prone to [off-by-one errors](https://en.wikipedia.org/wiki/Off-by-one_error)—particularly when used in a non-standard way.
-Fortunately, Smalltalk significantly improved this state of affairs with an idea called [list comprehensions](http://en.wikipedia.org/wiki/List_comprehension), which are commonly known today as `for/in` loops.
+Fortunately, Smalltalk significantly improved this state of affairs with an idea called [list comprehensions](https://en.wikipedia.org/wiki/List_comprehension), which are commonly known today as `for/in` loops.
## List Comprehension (`for/in`)
By using a higher level of abstraction, declaring the intention of iterating through all elements of a collection, not only are we less prone to error, but there's a lot less to type:
-~~~{objective-c}
+```objc
for (id object in array) {
NSLog(@"%@", object);
}
-~~~
+```
In Cocoa, comprehensions are available to any class that implements the `NSFastEnumeration` protocol, including `NSArray`, `NSSet`, and `NSDictionary`.
@@ -52,11 +52,11 @@ In Cocoa, comprehensions are available to any class that implements the `NSFastE
`NSFastEnumeration` contains a single method:
-~~~{objective-c}
+```objc
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
objects:(id *)stackbuf
count:(NSUInteger)len
-~~~
+```
> - `state`: Context information that is used in the enumeration to, in addition to other possibilities, ensure that the collection has not been mutated.
> - `stackbuf`: A C array of objects over which the sender is to iterate.
@@ -66,14 +66,14 @@ One single, _deceptively complicated_ method. There's that `stackbuf` out pointe
### `NSFastEnumerationState`
-~~~{objective-c}
+```objc
typedef struct {
unsigned long state;
id *itemsPtr;
unsigned long *mutationsPtr;
unsigned long extra[5];
} NSFastEnumerationState;
-~~~
+```
> - `state`: Arbitrary state information used by the iterator. Typically this is set to 0 at the beginning of the iteration.
> - `itemsPtr`: A C array of objects.
@@ -94,30 +94,30 @@ But of course, before `NSFastEnumeration` (circa OS X Leopard / iOS 2.0), there
For the uninitiated, `NSEnumerator` is an abstract class that implements two methods:
-~~~{objective-c}
+```objc
- (id)nextObject
- (NSArray *)allObjects
-~~~
+```
`nextObject` returns the next object in the collection, or `nil` if unavailable. `allObjects` returns all of the remaining objects, if any. `NSEnumerator`s can only go forward, and only in single increments.
To enumerate through all elements in a collection, one would use `NSEnumerator` thusly:
-~~~{objective-c}
+```objc
id object = nil;
NSEnumerator *enumerator = ...;
while ((object = [enumerator nextObject])) {
NSLog(@"%@", object);
}
-~~~
+```
...or because `NSEnumerator` itself conforms to `` in an attempt to stay hip to the way kids do things these days:
-~~~{objective-c}
+```objc
for (id object in enumerator) {
NSLog(@"%@", object);
}
-~~~
+```
If you're looking for a convenient way to add fast enumeration to your own non-collection-class-backed objects, `NSEnumerator` is likely a much more palatable option than getting your hands messy with `NSFastEnumeration`'s implementation details.
@@ -131,11 +131,11 @@ Some quick points of interest about `NSEnumeration`:
Finally, with the introduction of blocks in OS X Snow Leopard / iOS 4, came a new block-based way to enumerate collections:
-~~~{objective-c}
+```objc
[array enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
NSLog(@"%@", object);
}];
-~~~
+```
Collection classes like `NSArray`, `NSSet`, `NSDictionary`, and `NSIndexSet` include a similar set of block enumeration methods.
@@ -145,20 +145,20 @@ Unless you actually need the numerical index while iterating, it's almost always
One last thing to be aware of are the expanded method variants with an `options` parameter:
-~~~{objective-c}
+```objc
- (void)enumerateObjectsWithOptions:(NSEnumerationOptions)opts
usingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block
-~~~
+```
### `NSEnumerationOptions`
-~~~{objective-c}
+```objc
enum {
NSEnumerationConcurrent = (1UL << 0),
NSEnumerationReverse = (1UL << 1),
};
typedef NSUInteger NSEnumerationOptions;
-~~~
+```
> - `NSEnumerationConcurrent`: Specifies that the Block enumeration should be concurrent. The order of invocation is nondeterministic and undefined; this flag is a hint and may be ignored by the implementation under some circumstances; the code of the Block must be safe against concurrent invocation.
> - `NSEnumerationReverse`: Specifies that the enumeration should be performed in reverse. This option is available for `NSArray` and `NSIndexSet` classes; its behavior is undefined for `NSDictionary` and `NSSet` classes, or when combined with the `NSEnumerationConcurrent` flag.
diff --git a/2013-07-08-nsexpression.md b/2013-07-08-nsexpression.md
index 0fd177b9..397aca90 100644
--- a/2013-07-08-nsexpression.md
+++ b/2013-07-08-nsexpression.md
@@ -1,6 +1,6 @@
---
title: NSExpression
-author: Mattt Thompson
+author: Mattt
category: Cocoa
tags: nshipster
excerpt: "Cocoa is the envy of other standard libraries when it comes to querying and arranging information. With NSPredicate, NSSortDescriptor, and an occasional NSFetchRequest, even the most complex data tasks can be reduced into just a few, extremely-understandable lines of code."
@@ -9,7 +9,7 @@ status:
reviewed: September 8, 2015
---
-Cocoa is the envy of other standard libraries when it comes to querying and arranging information. With `NSPredicate`, [`NSSortDescriptor`](http://nshipster.com/nssortdescriptor/), and an occasional `NSFetchRequest`, even the most complex data tasks can be reduced into just a few, _extremely-understandable_ lines of code.
+Cocoa is the envy of other standard libraries when it comes to querying and arranging information. With `NSPredicate`, [`NSSortDescriptor`](https://nshipster.com/nssortdescriptor/), and an occasional `NSFetchRequest`, even the most complex data tasks can be reduced into just a few, _extremely-understandable_ lines of code.
Now, NSHipsters are no doubt already familiar with `NSPredicate` (and if you aren't, be sure to tune in next week!), but if we take a closer look at `NSPredicate`, we see that `NSPredicate` is actually made up of smaller, atomic parts: two `NSExpression`s (a left-hand value & a right-hand value), compared with an operator (e.g. `<`, `IN`, `LIKE`, etc.).
@@ -28,10 +28,10 @@ let mathExpression = NSExpression(format: "4 + 5 - 2**3")
let mathValue = mathExpression.expressionValueWithObject(nil, context: nil) as? Int
// 1
```
-~~~ objective-c
+``` objective-c
NSExpression *expression = [NSExpression expressionWithFormat:@"4 + 5 - 2**3"];
id value = [expression expressionValueWithObject:nil context:nil]; // => 1
-~~~
+```
It's no [Wolfram Alpha](http://www.wolframalpha.com/input/?i=finn+the+human+like+curve), but if your app does anything where evaluating mathematical expressions would be useful, well... there you go.
@@ -45,17 +45,17 @@ let statsExpression = NSExpression(forFunction:"stddev:", arguments:[NSExpressio
let statsValue = statsExpression.expressionValueWithObject(nil, context: nil) as? Double
// 3.21859...
```
-~~~ objective-c
+``` objective-c
NSArray *numbers = @[@1, @2, @3, @4, @4, @5, @9, @11];
NSExpression *expression = [NSExpression expressionForFunction:@"stddev:" arguments:@[[NSExpression expressionForConstantValue:numbers]]];
id value = [expression expressionValueWithObject:nil context:nil]; // => 3.21859...
-~~~
+```
> `NSExpression` functions take a given number of sub-expression arguments. For instance, in the above example, to get the standard deviation of the collection, the array of numbers had to be wrapped with `+expressionForConstantValue:`. A minor inconvenience (which ultimately allows `NSExpression` to be incredibly flexible), but enough to trip up anyone trying things out for the first time.
-If you found the [Key-Value Coding Simple Collection Operators](http://nshipster.com/kvc-collection-operators/) (`@avg`, `@sum`, et al.) lacking, perhaps `NSExpression`'s built-in statistical, arithmetic, and bitwise functions will pique your interest.
+If you found the [Key-Value Coding Simple Collection Operators](https://nshipster.com/kvc-collection-operators/) (`@avg`, `@sum`, et al.) lacking, perhaps `NSExpression`'s built-in statistical, arithmetic, and bitwise functions will pique your interest.
-> **A word of caution**: [according to this table in Apple's documentation for `NSExpression`](http://developer.apple.com/library/ios/#documentation/cocoa/reference/foundation/Classes/NSExpression_Class/Reference/NSExpression.html), there is apparently no overlap between the availability of functions between OS X & iOS. It would appear that recent versions of iOS do, indeed, support functions like `stddev:`, but this is not reflected in headers or documentation. Any details [in the form of a pull request](https://github.com/NSHipster/articles/pulls) would be greatly appreciated.
+> **A word of caution**: [according to this table in Apple's documentation for `NSExpression`](https://developer.apple.com/library/ios/#documentation/cocoa/reference/foundation/Classes/NSExpression_Class/Reference/NSExpression.html), there is apparently no overlap between the availability of functions between OS X & iOS. It would appear that recent versions of iOS do, indeed, support functions like `stddev:`, but this is not reflected in headers or documentation. Any details [in the form of a pull request](https://github.com/NSHipster/articles/pulls) would be greatly appreciated.
### Statistics
@@ -100,10 +100,10 @@ So mentioned, because `ceiling` is easily confused with `ceil(3)`. Whereas `ceil
### Random Functions
-Two variations—one with and one without an argument. Taking no argument, `random` returns an equivalent of `rand()`, while `randomn:` takes a single number and returns an equivalent of `rand(3)`.
+Two variations—one with and one without an argument. Taking no argument, `random` returns an equivalent of `rand(3)`, while `random:` takes a random element from the `NSExpression` of an array of numbers.
- `random`
-- `randomn:`
+- `random:`
### Binary Arithmetic
@@ -140,7 +140,7 @@ extension NSNumber {
}
}
```
-~~~ objective-c
+``` objective-c
@interface NSNumber (Factorial)
- (NSNumber *)factorial;
@end
@@ -150,7 +150,7 @@ extension NSNumber {
return @(tgamma([self doubleValue] + 1));
}
@end
-~~~
+```
Then, use the function thusly (the `FUNCTION()` macro in `+expressionWithFormat:` is shorthand for the process of building out with `-expressionForFunction:`, et al.):
@@ -159,10 +159,10 @@ let functionExpression = NSExpression(format:"FUNCTION(4.2, 'factorial')")
let functionValue = functionExpression.expressionValueWithObject(nil, context: nil) as? Double
// 32.578...
```
-~~~ objective-c
+``` objective-c
NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION(4.2, 'factorial')"];
id value = [expression expressionValueWithObject:nil context:nil]; // 32.578...
-~~~
+```
The advantage here, over calling `-factorial` directly is the ability to invoke the function in an `NSPredicate` query. For example, a `location:withinRadius:` method might be defined to easily query managed objects nearby a user's current location.
diff --git a/2013-07-15-nspredicate.md b/2013-07-15-nspredicate.md
index 40d34db2..4694191a 100644
--- a/2013-07-15-nspredicate.md
+++ b/2013-07-15-nspredicate.md
@@ -1,27 +1,27 @@
---
title: NSPredicate
-author: Mattt Thompson
+author: Mattt
category: Cocoa
tags: nshipster, popular
excerpt: "NSPredicate is a Foundation class that specifies how data should be fetched or filtered. Its query language, which is like a cross between a SQL WHERE clause and a regular expression, provides an expressive, natural language interface to define logical conditions on which a collection is searched."
status:
- swift: 2.0
- reviewed: September 19, 2015
+ swift: 4.2
+ reviewed: January 9, 2019
---
`NSPredicate` is a Foundation class that specifies how data should be fetched or filtered. Its query language, which is like a cross between a SQL `WHERE` clause and a regular expression, provides an expressive, natural language interface to define logical conditions on which a collection is searched.
-It's easier to show `NSPredicate` in use, rather than talk about it in the abstract, so we're going to revisit the example data set used in the [`NSSortDescriptor` article](http://nshipster.com/nssortdescriptor/):
+It's easier to show `NSPredicate` in use, rather than talk about it in the abstract, so we're going to revisit the example data set used in the [`NSSortDescriptor` article](https://nshipster.com/nssortdescriptor/):
| `firstName` | `lastName` | `age` |
-|-------------|------------|-------|
+| ----------- | ---------- | ----- |
| Alice | Smith | 24 |
| Bob | Jones | 27 |
| Charlie | Smith | 33 |
| Quentin | Alberts | 31 |
-~~~{swift}
-class Person: NSObject {
+```swift
+@objcMembers class Person: NSObject {
let firstName: String
let lastName: String
let age: Int
@@ -41,23 +41,23 @@ let alice = Person(firstName: "Alice", lastName: "Smith", age: 24)
let bob = Person(firstName: "Bob", lastName: "Jones", age: 27)
let charlie = Person(firstName: "Charlie", lastName: "Smith", age: 33)
let quentin = Person(firstName: "Quentin", lastName: "Alberts", age: 31)
-let people = [alice, bob, charlie, quentin]
+let people = [alice, bob, charlie, quentin] as NSArray
let bobPredicate = NSPredicate(format: "firstName = 'Bob'")
let smithPredicate = NSPredicate(format: "lastName = %@", "Smith")
let thirtiesPredicate = NSPredicate(format: "age >= 30")
-(people as NSArray).filteredArrayUsingPredicate(bobPredicate)
+people.filtered(using: bobPredicate)
// ["Bob Jones"]
-(people as NSArray).filteredArrayUsingPredicate(smithPredicate)
+people.filtered(using: smithPredicate)
// ["Alice Smith", "Charlie Smith"]
-(people as NSArray).filteredArrayUsingPredicate(thirtiesPredicate)
+people.filtered(using: thirtiesPredicate)
// ["Charlie Smith", "Quentin Alberts"]
-~~~
+```
-~~~{objective-c}
+```objc
@interface Person : NSObject
@property NSString *firstName;
@property NSString *lastName;
@@ -99,7 +99,7 @@ NSLog(@"Smiths: %@", [people filteredArrayUsingPredicate:smithPredicate]);
// ["Charlie Smith", "Quentin Alberts"]
NSLog(@"30's: %@", [people filteredArrayUsingPredicate:thirtiesPredicate]);
-~~~
+```
## Using `NSPredicate` with Collections
@@ -122,35 +122,35 @@ Mutable collections, `NSMutableArray` & `NSMutableSet` have the method `filterUs
> - `%@` is a var arg substitution for an object value—often a string, number, or date.
> - `%K` is a var arg substitution for a key path.
-~~~{swift}
+```swift
let ageIs33Predicate = NSPredicate(format: "%K = %@", "age", "33")
-(people as NSArray).filteredArrayUsingPredicate(ageIs33Predicate)
+people.filtered(using: ageIs33Predicate)
// ["Charlie Smith"]
-~~~
+```
-~~~{objective-c}
+```objc
NSPredicate *ageIs33Predicate = [NSPredicate predicateWithFormat:@"%K = %@", @"age", @33];
// ["Charlie Smith"]
NSLog(@"Age 33: %@", [people filteredArrayUsingPredicate:ageIs33Predicate]);
-~~~
+```
> - `$VARIABLE_NAME` is a value that can be substituted with `NSPredicate -predicateWithSubstitutionVariables:`.
-~~~{swift}
+```swift
let namesBeginningWithLetterPredicate = NSPredicate(format: "(firstName BEGINSWITH[cd] $letter) OR (lastName BEGINSWITH[cd] $letter)")
-(people as NSArray).filteredArrayUsingPredicate(namesBeginningWithLetterPredicate.predicateWithSubstitutionVariables(["letter": "A"]))
+people.filtered(using: namesBeginningWithLetterPredicate.withSubstitutionVariables(["letter": "A"]))
// ["Alice Smith", "Quentin Alberts"]
-~~~
+```
-~~~{objective-c}
+```objc
NSPredicate *namesBeginningWithLetterPredicate = [NSPredicate predicateWithFormat:@"(firstName BEGINSWITH[cd] $letter) OR (lastName BEGINSWITH[cd] $letter)"];
// ["Alice Smith", "Quentin Alberts"]
NSLog(@"'A' Names: %@", [people filteredArrayUsingPredicate:[namesBeginningWithLetterPredicate predicateWithSubstitutionVariables:@{@"letter": @"A"}]]);
-~~~
+```
### Basic Comparisons
@@ -170,13 +170,13 @@ NSLog(@"'A' Names: %@", [people filteredArrayUsingPredicate:[namesBeginningWithL
### String Comparisons
-> String comparisons are by default case and diacritic sensitive. You can modify an operator using the key characters c and d within square braces to specify case and diacritic insensitivity respectively, for example firstName BEGINSWITH[cd] $FIRST_NAME.
+> String comparisons are by default case and diacritic sensitive. You can modify an operator using the key characters c and d within square braces to specify case and diacritic insensitivity respectively, for example firstName BEGINSWITH[cd] \$FIRST_NAME.
> - `BEGINSWITH`: The left-hand expression begins with the right-hand expression.
> - `CONTAINS`: The left-hand expression contains the right-hand expression.
> - `ENDSWITH`: The left-hand expression ends with the right-hand expression.
> - `LIKE`: The left hand expression equals the right-hand expression: `?` and `*` are allowed as wildcard characters, where `?` matches 1 character and `*` matches 0 or more characters.
-> - `MATCHES`: The left hand expression equals the right hand expression using a regex-style comparison according to ICU v3 (for more details see the [ICU User Guide for Regular Expressions](http://userguide.icu-project.org/strings/regexp)).
+> - `MATCHES`: The left hand expression equals the right hand expression using a regex-style comparison according to ICU v3 (for more details see the [ICU User Guide for Regular Expressions](http://userguide.icu-project.org/strings/regexp)).
### Aggregate Operations
@@ -205,34 +205,46 @@ We saw that `AND` & `OR` can be used in predicate format strings to create compo
For example, the following predicates are equivalent:
-~~~{swift}
-NSCompoundPredicate(type: .AndPredicateType, subpredicates: [NSPredicate(format: "age > 25"), NSPredicate(format: "firstName = %@", "Quentin")])
+```swift
+NSCompoundPredicate(
+ type: .and,
+ subpredicates: [
+ NSPredicate(format: "age > 25"),
+ NSPredicate(format: "firstName = %@", "Quentin")
+ ]
+)
NSPredicate(format: "(age > 25) AND (firstName = %@)", "Quentin")
-~~~
+```
-~~~{objective-c}
+```objc
[NSCompoundPredicate andPredicateWithSubpredicates:@[[NSPredicate predicateWithFormat:@"age > 25"], [NSPredicate predicateWithFormat:@"firstName = %@", @"Quentin"]]];
[NSPredicate predicateWithFormat:@"(age > 25) AND (firstName = %@)", @"Quentin"];
-~~~
+```
While the syntax string literal is certainly easier to type, there are occasions where you may need to combine existing predicates. In these cases, `NSCompoundPredicate -andPredicateWithSubpredicates:` & `-orPredicateWithSubpredicates:` is the way to go.
## `NSComparisonPredicate`
-Similarly, if after reading [last week's article](http://nshipster.com/nsexpression/) you now find yourself with more `NSExpression` objects than you know what to do with, `NSComparisonPredicate` can help you out.
+Similarly, if after reading [last week's article](https://nshipster.com/nsexpression/) you now find yourself with more `NSExpression` objects than you know what to do with, `NSComparisonPredicate` can help you out.
Like `NSCompoundPredicate`, `NSComparisonPredicate` constructs an `NSPredicate` from subcomponents—in this case, `NSExpression`s on the left and right hand sides.
Analyzing its class constructor provides a glimpse into the way `NSPredicate` format strings are parsed:
-~~~{objective-c}
+```objc
+ (NSPredicate *)predicateWithLeftExpression:(NSExpression *)lhs
rightExpression:(NSExpression *)rhs
modifier:(NSComparisonPredicateModifier)modifier
type:(NSPredicateOperatorType)type
options:(NSUInteger)options
-~~~
+```
+
+```swift
+init(leftExpression lhs: NSExpression,
+ rightExpression rhs: NSExpression,
+customSelector selector: Selector)
+```
#### Parameters
@@ -244,26 +256,26 @@ Analyzing its class constructor provides a glimpse into the way `NSPredicate` fo
### `NSComparisonPredicate` Types
-~~~{swift}
-public enum NSPredicateOperatorType : UInt {
- case LessThanPredicateOperatorType
- case LessThanOrEqualToPredicateOperatorType
- case GreaterThanPredicateOperatorType
- case GreaterThanOrEqualToPredicateOperatorType
- case EqualToPredicateOperatorType
- case NotEqualToPredicateOperatorType
- case MatchesPredicateOperatorType
- case LikePredicateOperatorType
- case BeginsWithPredicateOperatorType
- case EndsWithPredicateOperatorType
- case InPredicateOperatorType
- case CustomSelectorPredicateOperatorType
- case ContainsPredicateOperatorType
- case BetweenPredicateOperatorType
+```swift
+enum NSComparisonPredicate.Operator: UInt {
+ case lessThan
+ case lessThanOrEqualTo
+ case greaterThan
+ case greaterThanOrEqualTo
+ case equalTo
+ case notEqualTo
+ case matches
+ case like
+ case beginsWith
+ case endsWith
+ case `in`
+ case customSelector
+ case contains
+ case between
}
-~~~
+```
-~~~{objective-c}
+```objc
enum {
NSLessThanPredicateOperatorType = 0,
NSLessThanOrEqualToPredicateOperatorType,
@@ -282,36 +294,36 @@ enum {
};
typedef NSUInteger NSPredicateOperatorType;
-~~~
+```
### `NSComparisonPredicate` Options
-> - `NSCaseInsensitivePredicateOption`: A case-insensitive predicate. You represent this option in a predicate format string using a [c] following a string operation (for example, "NeXT" like[c] "next").
-> - `NSDiacriticInsensitivePredicateOption`: A diacritic-insensitive predicate. You represent this option in a predicate format string using a [d] following a string operation (for example, "naïve" like[d] "naive").
-> - `NSNormalizedPredicateOption`: Indicates that the strings to be compared have been preprocessed. This option supersedes NSCaseInsensitivePredicateOption and NSDiacriticInsensitivePredicateOption, and is intended as a performance optimization option. You represent this option in a predicate format string using a [n] following a string operation (for example, "WXYZlan" matches[n] ".lan").
-> - `NSLocaleSensitivePredicateOption`: Indicates that strings to be compared using `<`, `<=`, `=`, `=>`, `>` should be handled in a locale-aware fashion. You represent this option in a predicate format string using a `[l]` following one of the `<`, `<=`, `=`, `=>`, `>` operators (for example, "straße" >[l] "strasse").
+> - `NSCaseInsensitivePredicateOption`: A case-insensitive predicate. You represent this option in a predicate format string using a [c] following a string operation (for example, `"NeXT" like[c] "next"`).
+> - `NSDiacriticInsensitivePredicateOption`: A diacritic-insensitive predicate. You represent this option in a predicate format string using a [d] following a string operation (for example, `"naïve" like[d] "naive"`).
+> - `NSNormalizedPredicateOption`: Indicates that the strings to be compared have been preprocessed. This option supersedes NSCaseInsensitivePredicateOption and NSDiacriticInsensitivePredicateOption, and is intended as a performance optimization option. You represent this option in a predicate format string using a [n] following a string operation (for example, `"WXYZlan" matches[n] ".lan"`).
+> - `NSLocaleSensitivePredicateOption`: Indicates that strings to be compared using `<`, `<=`, `=`, `=>`, `>` should be handled in a locale-aware fashion. You represent this option in a predicate format string using a `[l]` following one of the `<`, `<=`, `=`, `=>`, `>` operators (for example, `"straße" >[l] "strasse"`).
## Block Predicates
Finally, if you just can't be bothered to learn the `NSPredicate` format syntax, you can go through the motions with `NSPredicate +predicateWithBlock:`.
-~~~{swift}
+```swift
let shortNamePredicate = NSPredicate { (evaluatedObject, _) in
return (evaluatedObject as! Person).firstName.utf16.count <= 5
}
-(people as NSArray).filteredArrayUsingPredicate(shortNamePredicate)
+people.filtered(using: shortNamePredicate)
// ["Alice Smith", "Bob Jones"]
-~~~
+```
-~~~{objective-c}
+```objc
NSPredicate *shortNamePredicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
- return [[evaluatedObject firstName] length] <= 5;
- }];
+ return [[evaluatedObject firstName] length] <= 5;
+}];
// ["Alice Smith", "Bob Jones"]
NSLog(@"Short Names: %@", [people filteredArrayUsingPredicate:shortNamePredicate]);
-~~~
+```
...Alright, that whole dig on `predicateWithBlock:` as being the lazy way out wasn't _entirely_ charitable.
diff --git a/2013-07-22-uimenucontroller.md b/2013-07-22-uimenucontroller.md
index 3fb5a3b3..6052e0fa 100644
--- a/2013-07-22-uimenucontroller.md
+++ b/2013-07-22-uimenucontroller.md
@@ -1,6 +1,6 @@
---
title: UIMenuController
-author: Mattt Thompson
+author: Mattt
category: Cocoa
tags: nshipster
excerpt: "Mobile usability today is truly quite remarkable—especially considering how far it's come in just the last decade. What was once a clumsy technology relegated to the tech elite has now become the primary mode of computation for a significant portion of the general population."
@@ -15,7 +15,7 @@ Yet despite its advances, one can't help but feel occasionally... trapped.
All too often, there will be information on the screen that you _just can't access_. Whether its flight information stuck in a table view cell or an unlinked URL, users are forced to solve problems creatively for lack of a provided solution.
-In the past, we've mentioned [localization](http://nshipster.com/nslocalizedstring) and [accessibility](http://nshipster.com/uiaccessibility) as two factors that distinguish great apps from the rest of the pack. This week, we'll add another item to that list: **Edit Actions**.
+In the past, we've mentioned [localization](https://nshipster.com/nslocalizedstring) and [accessibility](https://nshipster.com/uiaccessibility) as two factors that distinguish great apps from the rest of the pack. This week, we'll add another item to that list: **Edit Actions**.
### Copy, Cut, Paste, Delete, Select
@@ -23,7 +23,7 @@ iOS 3's killer feature was undoubtedly push notifications, but the ability to co
This may be due to how cumbersome it is to implement. Let's look at a simple implementation, and then dive into some specifics about the APIs. First the label itself:
-~~~{swift}
+```swift
class HipsterLabel : UILabel {
override func canBecomeFirstResponder() -> Bool {
return true
@@ -39,8 +39,8 @@ class HipsterLabel : UILabel {
UIPasteboard.generalPasteboard().string = text
}
}
-~~~
-~~~{objective-c}
+```
+```objc
// HipsterLabel.h
@interface HipsterLabel : UILabel
@end
@@ -65,11 +65,11 @@ class HipsterLabel : UILabel {
}
@end
-~~~
+```
And with that out of the way, the view controller that uses it:
-~~~{swift}
+```swift
override func viewDidLoad() {
super.viewDidLoad()
@@ -93,8 +93,8 @@ func handleLongPressGesture(recognizer: UIGestureRecognizer) {
recognizerView.becomeFirstResponder()
}
}
-~~~
-~~~{objective-c}
+```
+```objc
- (void)viewDidLoad {
HipsterLabel *label = ...;
label.userInteractionEnabled = YES;
@@ -114,7 +114,7 @@ func handleLongPressGesture(recognizer: UIGestureRecognizer) {
[menuController setMenuVisible:YES animated:YES];
}
}
-~~~
+```
So, to recap, in order to allow a label's text to be copied, the following must happen:
diff --git a/2013-07-31-nshipster-quiz-3.md b/2013-07-31-nshipster-quiz-3.md
index 149ca87b..333cbacb 100644
--- a/2013-07-31-nshipster-quiz-3.md
+++ b/2013-07-31-nshipster-quiz-3.md
@@ -1,13 +1,13 @@
---
title: "NSHipster Quiz #3"
-author: Mattt Thompson
+author: Mattt
category: Trivia
excerpt: "Test your mettle as NSHipster Pub Quiz goes on the road, to New York City!"
status:
swift: n/a
---
-NSHipster Pub Quiz came to New York City on July 30th. Like our [first](http://nshipster.com/nshipster-quiz-1/) and [second](http://nshipster.com/nshipster-quiz-2/) quizzes, questions ranged from random Apple trivia to obscure framework questions—this time, with a particular focus on hardware rumors and questions about iOS [REDACTED].
+NSHipster Pub Quiz came to New York City on July 30th. Like our [first](https://nshipster.com/nshipster-quiz-1/) and [second](https://nshipster.com/nshipster-quiz-2/) quizzes, questions ranged from random Apple trivia to obscure framework questions—this time, with a particular focus on hardware rumors and questions about iOS [REDACTED].
The event was hosted by [Meetup](http://meetup.com), and sponsored by [Heroku](https://heroku.com). Dozens of the best and brightest minds in Objective-C attended the event, with the team "The Forstall Five" ([@mb](https://twitter.com/mb), [@bcapps](https://twitter.com/bcapps), [@ethicalpaul](https://twitter.com/ethicalpaul), [@grantjbutler](https://twitter.com/grantjbutler), and [@conbrolution](https://twitter.com/conbrolution)) taking first place.
@@ -62,43 +62,43 @@ With over 1 Million iOS & Mac Apps on the App Store, it's clear that the true se
- 1. What is the name of this iOS game?
-
+
- 2. What is the name of this iOS game?
-
+
- 3. What is the name of this iOS app?
-
+
- 4. What is the name of this popular iOS app?
-
+
- 5. While not on the App Store, jailbreakers will know this icon well. What's its name?
-
+
- 6. Which classic Mac app sports this delightful moving truck?
-
+
- 7. Which indispensible development tool has this incongruous icon?
-
+
- 8. Which app sports this sleek icon?
-
+
- 9. Which app is represented by this delightful mail bag?
-
+
- 10. Which (unfortunately stalled) app has this beautiful icon?
-
+
Round 4: [REDACTED]
@@ -131,10 +131,10 @@ Round 1: General Knowledge
3. [iPhone 5C](http://www.latimes.com/business/technology/la-fi-tn-the-iphone-5c-apple-color-plasticspotify-20130730,0,1720605.story)
4. [Aaron Sorkin](http://tech.fortune.cnn.com/2012/11/16/apple-sorkin-jobs-movie/)
5. [The Netherlands](http://appleinsider.com/articles/13/07/02/dutch-steve-jobs-schools-to-use-apples-ipad-for-entire-education-experience)
-6. [Douglas Engelbart](http://en.wikipedia.org/wiki/Douglas_Engelbart)
+6. [Douglas Engelbart](https://en.wikipedia.org/wiki/Douglas_Engelbart)
7. [HopStop.com](http://www.macworld.com/article/2044904/apple-finds-its-way-to-hopstop-acquisition.html)
-8. [Number of Stations (468)](http://en.wikipedia.org/wiki/New_York_City_Subway)
-9. [Jane Jacobs](http://en.wikipedia.org/wiki/The_Death_and_Life_of_Great_American_Cities)
+8. [Number of Stations (468)](https://en.wikipedia.org/wiki/New_York_City_Subway)
+9. [Jane Jacobs](https://en.wikipedia.org/wiki/The_Death_and_Life_of_Great_American_Cities)
10. [HBO Go, WatchESPN, Sky News, Qello, & CrunchyRoll](http://www.apple.com/pr/library/2013/06/19HBO-GO-WatchESPN-Come-to-Apple-TV.html)
Round 2: Public, Private, or Fake?
diff --git a/2013-08-05-documentation.md b/2013-08-05-objective-c-documentation.md
similarity index 86%
rename from 2013-08-05-documentation.md
rename to 2013-08-05-objective-c-documentation.md
index bcea59b0..cf2fb8e9 100644
--- a/2013-08-05-documentation.md
+++ b/2013-08-05-objective-c-documentation.md
@@ -1,17 +1,17 @@
---
-title: Documentation
-author: Mattt Thompson
+title: Objective-C Documentation
+author: Mattt
category: Objective-C
excerpt: "There's an adage among Cocoa developers that Objective-C's verbosity lends its code to being effectively self-documenting. Between longMethodNamesWithNamedParameters: and the explicit typing of those parameters, Objective-C methods don't leave much to the imagination."
status:
- swift: n/a
+ swift: n/a
---
There's an adage among Cocoa developers that Objective-C's verbosity lends its code to being effectively self-documenting. Between `longMethodNamesWithNamedParameters:` and the explicit typing of those parameters, Objective-C methods don't leave much to the imagination.
But even self-documenting code can be improved with documentation, and just a small amount of effort can yield significant benefit to others.
-**Listen**—I know programmers don't like to be told what to do, and prescriptive arguments of "thou shalt" and "thou shalt not" have the [rhetorical impact of a trombone](http://www.youtube.com/watch?v=ss2hULhXf04), so I'll cut to the chase:
+**Listen**—I know programmers don't like to be told what to do, and prescriptive arguments of "thou shalt" and "thou shalt not" have the [rhetorical impact of a trombone](https://www.youtube.com/watch?v=ss2hULhXf04), so I'll cut to the chase:
Do you like Apple's documentation? Don't you want that [for your own libraries?](http://cocoadocs.org/docsets/AFNetworking/1.3.1/Classes/AFHTTPClient.html) Follow just a few simple conventions, and your code can get the documentation it deserves.
@@ -19,7 +19,7 @@ Do you like Apple's documentation? Don't you want that [for your own libraries?]
Every modern programming language has comments: non-executable natural language annotations denoted by a special character sequence, such as `//`, `/* */`, `#`, and `--`. Documentation provides auxiliary explanation and context to code using specially-formatted comments, which can be extracted and parsed by a build tool.
-In Objective-C, the documentation tool of choice is [`appledoc`](https://github.com/tomaz/appledoc). Using a [Javadoc](http://en.wikipedia.org/wiki/Javadoc)-like syntax, `appledoc` is able to generate HTML and Xcode-compatible `.docset` docs from `.h` files that [look nearly identical](http://cocoadocs.org/docsets/AFNetworking/1.3.1/Classes/AFHTTPClient.html) to [Apple's official documentation](https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/NSArray.html).
+In Objective-C, the documentation tool of choice is [`appledoc`](https://github.com/tomaz/appledoc). Using a [Javadoc](https://en.wikipedia.org/wiki/Javadoc)-like syntax, `appledoc` is able to generate HTML and Xcode-compatible `.docset` docs from `.h` files that [look nearly identical](http://cocoadocs.org/docsets/AFNetworking/1.3.1/Classes/AFHTTPClient.html) to [Apple's official documentation](https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/NSArray.html).
> [Doxygen](http://www.stack.nl/~dimitri/doxygen/), used primarily for C++, is another viable option for Objective-C, but is generally dispreffered by the iOS / OS X developer community.
@@ -45,7 +45,7 @@ Each method should similarly begin with a concise description of its functionali
Properties are often described in a single sentence, and should include what its default value is.
-Related properties and methods should be grouped by an `@name` declaration, which functions similarly to a [`#pragma mark`](http://nshipster.com/pragma/), and can be used with the triple-slash (`///`) comment variant.
+Related properties and methods should be grouped by an `@name` declaration, which functions similarly to a [`#pragma mark`](https://nshipster.com/pragma/), and can be used with the triple-slash (`///`) comment variant.
Try reading other documentation before writing some yourself, in order to get a sense of the correct tone and style. When in doubt about terminology or verbiage, follow the lead of the closest thing you can find from Apple's official docs.
diff --git a/2013-08-12-random.md b/2013-08-12-random.md
index e98135ba..94904a39 100644
--- a/2013-08-12-random.md
+++ b/2013-08-12-random.md
@@ -1,6 +1,6 @@
---
title: rand(3) / random(3) / arc4random(3) / et al.
-author: Mattt Thompson
+author: Mattt
category: Objective-C
excerpt: "What passes for randomness is merely a hidden chain of causality. Of course, app developers could care less about philosophy—what they want is code. Thus, our goal this week: to clear up all of the lingering questions and misunderstandings about doing random things in Objective-C"
status:
@@ -13,7 +13,7 @@ In a mechanical universe of material interactions expressed through mathematical
We can be sure of one thing, however: in the closed, digital universe of CPU cycles, processes, and threads, there is no true randomness, only _pseudorandomness_.
-Pseudorandomness, is often implemented in a way very similar to a [cryptographic hash](http://en.wikipedia.org/wiki/Cryptographic_hash_function), as a deterministic function that returns a value based on the current time (salted, of course, by some initial seed value). Also like hash functions, there are a number of PRNG, or pseudorandom number generators, each of which are optimized for particular performance characteristics: uniformity, periodicity, and computational complexity.
+Pseudorandomness, is often implemented in a way very similar to a [cryptographic hash](https://en.wikipedia.org/wiki/Cryptographic_hash_function), as a deterministic function that returns a value based on the current time (salted, of course, by some initial seed value). Also like hash functions, there are a number of PRNG, or pseudorandom number generators, each of which are optimized for particular performance characteristics: uniformity, periodicity, and computational complexity.
Of course, for app developers, all of this is an academic exercise. And rather than bore you with any more high-minded, long-winded treatises on the philosophical nature of randomness, we're going to tackle this one FAQ-style.
@@ -29,24 +29,24 @@ Specifically, to generate a random number between `0` and `N - 1`, use `arc4rand
### Random `int` between `0` and `N - 1`
-~~~{objective-c}
+```objc
NSUInteger r = arc4random_uniform(N);
-~~~
+```
### Random `int` between `1` and `N`
-~~~{objective-c}
+```objc
NSUInteger r = arc4random_uniform(N) + 1;
-~~~
+```
### Random `double` between `0` and `1`
If you are generating a random `double` or `float`, another good option is the more obscure `rand48` family of functions, including `drand48(3)`.
-~~~{objective-c}
+```objc
srand48(time(0));
double r = drand48();
-~~~
+```
> `rand48` functions, unlike `arc4random` functions, require an initial value to be seeded before generating random numbers. This seed function, `srand48(3)`, should only be run once.
@@ -54,18 +54,18 @@ double r = drand48();
Use `arc4random_uniform(3)` to generate a random number in the range of a non-empty array.
-~~~{objective-c}
+```objc
if ([array count] > 0) {
id obj = array[arc4random_uniform([array count])];
}
-~~~
+```
## How Do I Randomly Order an `NSArray`?
-~~~{objective-c}
+```objc
NSMutableArray *mutableArray = [NSMutableArray arrayWithArray:array];
NSUInteger count = [mutableArray count];
-// See http://en.wikipedia.org/wiki/Fisher–Yates_shuffle
+// See https://en.wikipedia.org/wiki/Fisher–Yates_shuffle
if (count > 1) {
for (NSUInteger i = count - 1; i > 0; --i) {
[mutableArray exchangeObjectAtIndex:i withObjectAtIndex:arc4random_uniform((int32_t)(i + 1))];
@@ -73,13 +73,13 @@ if (count > 1) {
}
NSArray *randomArray = [NSArray arrayWithArray:mutableArray];
-~~~
+```
> This code is borrowed from [TTTRandomizedEnumerator](https://github.com/mattt/TTTRandomizedEnumerator), which also provides randomized enumerators for `NSSet`, `NSOrderedSet`, and `NSDictionary`.
## How Do I Generate a Random String?
-If you're looking to generate "[lorem ipsum](http://en.wikipedia.org/wiki/Lorem_ipsum)"-style sentences, try constructing a [Markov Chain](http://en.wikipedia.org/wiki/Markov_chain) from a [corpus](http://en.wikipedia.org/wiki/Text_corpus).
+If you're looking to generate "[lorem ipsum](https://en.wikipedia.org/wiki/Lorem_ipsum)"-style sentences, try constructing a [Markov Chain](https://en.wikipedia.org/wiki/Markov_chain) from a [corpus](https://en.wikipedia.org/wiki/Text_corpus).
Otherwise, if you're looking to just get random letters, try one of the following methods:
@@ -87,22 +87,22 @@ Otherwise, if you're looking to just get random letters, try one of the followin
If you are operating on a known, contiguous range of Unicode characters, such as the lowercase letters (`U+0061` — `U+007A`), you can do a simple conversion from a `char`:
-~~~{objective-c}
+```objc
NSString *letter = [NSString stringWithFormat:@"%c", arc4random_uniform(26) + 'a'];
-~~~
+```
### Pick a Random Character From an `NSString`
Otherwise, a simple way to pick random letters from a set of your choosing is to simply create a string containing all of the possible letters:
-~~~{objective-c}
+```objc
NSString *vowels = @"aeiouy";
NSString *letter = [vowels substringWithRange:NSMakeRange(arc4random_uniform([vowels length]), 1)];
-~~~
+```
## Why Should I Use `arc4random(3)` instead of `rand(3)` or `random(3)`?
-> C functions are typically denoted with a number `3` inside of parentheses, following the organizational convention of [`man` pages](http://en.wikipedia.org/wiki/Man_page#Manual_sections).
+> C functions are typically denoted with a number `3` inside of parentheses, following the organizational convention of [`man` pages](https://en.wikipedia.org/wiki/Man_page#Manual_sections).
- `arc4random` does not require an initial seed (with `srand` or `srandom`), making it that much easier to use.
- `arc4random` has a range up to `0x100000000 (4294967296)`, whereas `rand` and `random` top out at `RAND_MAX = 0x7fffffff (2147483647)`.
diff --git a/2013-08-19-nshashtable-and-nsmaptable.md b/2013-08-19-nshashtable-and-nsmaptable.md
index 1cbe0620..65eed7c8 100644
--- a/2013-08-19-nshashtable-and-nsmaptable.md
+++ b/2013-08-19-nshashtable-and-nsmaptable.md
@@ -1,6 +1,6 @@
---
title: "NSHashTable & NSMapTable"
-author: Mattt Thompson
+author: Mattt
category: Cocoa
tags: nshipster
excerpt: "NSSet and NSDictionary, along with NSArray are the workhorse collection classes of Foundation. Unlike other standard libraries, implementation details are hidden from developers, allowing them to write simple code and trust that it will be (reasonably) performant."
@@ -9,11 +9,11 @@ status:
reviewed: September 11, 2015
---
-`NSSet` and `NSDictionary`, along with `NSArray` are the workhorse collection classes of Foundation. Unlike [ other standard libraries](http://en.wikipedia.org/wiki/Java_collections_framework), implementation details are [hidden](http://ridiculousfish.com/blog/posts/array.html) from developers, allowing them to write simple code and trust that it will be (reasonably) performant.
+`NSSet` and `NSDictionary`, along with `NSArray` are the workhorse collection classes of Foundation. Unlike [ other standard libraries](https://en.wikipedia.org/wiki/Java_collections_framework), implementation details are [hidden](http://ridiculousfish.com/blog/posts/array.html) from developers, allowing them to write simple code and trust that it will be (reasonably) performant.
However, even the best abstractions break down; their underlying assumptions overturned. In these cases, developers either venture further down the abstraction, or, if available use a more general-purpose solution.
-For `NSSet` and `NSDictionary`, the breaking assumption was in the memory behavior when storing objects in the collection. For `NSSet`, objects are a strongly referenced, as are `NSDictionary` values. Keys, on the other hand, are copied by `NSDictionary`. If a developer wanted to store a weak value, or use a non-``-conforming object as a key, they could be clever and use [`NSValue +valueWithNonretainedObject`](http://nshipster.com/nsvalue/). Or, as of iOS 6 (and as far back as OS X Leopard), they could use `NSHashTable` or `NSMapTable`, the more general-case counterparts to `NSSet` or `NSDictionary`, respectively.
+For `NSSet` and `NSDictionary`, the breaking assumption was in the memory behavior when storing objects in the collection. For `NSSet`, objects are a strongly referenced, as are `NSDictionary` values. Keys, on the other hand, are copied by `NSDictionary`. If a developer wanted to store a weak value, or use a non-``-conforming object as a key, they could be clever and use [`NSValue +valueWithNonretainedObject`](https://nshipster.com/nsvalue/). Or, as of iOS 6 (and as far back as OS X Leopard), they could use `NSHashTable` or `NSMapTable`, the more general-case counterparts to `NSSet` or `NSDictionary`, respectively.
So without further ado, here's everything you need to know about two of the more obscure members of Foundation's collection classes:
@@ -29,29 +29,29 @@ So without further ado, here's everything you need to know about two of the more
### Usage
-~~~{swift}
+```swift
let hashTable = NSHashTable(options: .CopyIn)
hashTable.addObject("foo")
hashTable.addObject("bar")
hashTable.addObject(42)
hashTable.removeObject("bar")
print("Members: \(hashTable.allObjects)")
-~~~
-~~~{objective-c}
+```
+```objc
NSHashTable *hashTable = [NSHashTable hashTableWithOptions:NSPointerFunctionsCopyIn];
[hashTable addObject:@"foo"];
[hashTable addObject:@"bar"];
[hashTable addObject:@42];
[hashTable removeObject:@"bar"];
NSLog(@"Members: %@", [hashTable allObjects]);
-~~~
+```
-`NSHashTable` objects are initialized with an option for any of the following behaviors. Deprecated enum values are due to `NSHashTable` being ported from Garbage-Collected OS X to ARC-ified iOS. Other values are aliased to options defined by [NSPointerFunctions](http://developer.apple.com/library/ios/DOCUMENTATION/Cocoa/Reference/Foundation/Classes/NSPointerFunctions_Class/Introduction/Introduction.html), which will be covered next week on NSHipster.
+`NSHashTable` objects are initialized with an option for any of the following behaviors. Deprecated enum values are due to `NSHashTable` being ported from Garbage-Collected OS X to ARC-ified iOS. Other values are aliased to options defined by [NSPointerFunctions](https://developer.apple.com/library/ios/DOCUMENTATION/Cocoa/Reference/Foundation/Classes/NSPointerFunctions_Class/Introduction/Introduction.html), which will be covered next week on NSHipster.
> - `NSHashTableStrongMemory`: Equal to `NSPointerFunctionsStrongMemory`. This is the default behavior, equivalent to `NSSet` member storage.
> - `NSHashTableWeakMemory`: Equal to `NSPointerFunctionsWeakMemory`. Uses weak read and write barriers. Using `NSPointerFunctionsWeakMemory`, object references will turn to `NULL` on last release.
> - `NSHashTableZeroingWeakMemory`: This option has been deprecated. Instead use the `NSHashTableWeakMemory` option.
-> - `NSHashTableCopyIn`: Use the memory acquire function to allocate and copy items on input (see [`NSPointerFunction -acquireFunction`](http://developer.apple.com/library/ios/DOCUMENTATION/Cocoa/Reference/Foundation/Classes/NSPointerFunctions_Class/Introduction/Introduction.html#//apple_ref/occ/instp/NSPointerFunctions/acquireFunction)). Equal to `NSPointerFunctionsCopyIn`.
+> - `NSHashTableCopyIn`: Use the memory acquire function to allocate and copy items on input (see [`NSPointerFunction -acquireFunction`](https://developer.apple.com/library/ios/DOCUMENTATION/Cocoa/Reference/Foundation/Classes/NSPointerFunctions_Class/Introduction/Introduction.html#//apple_ref/occ/instp/NSPointerFunctions/acquireFunction)). Equal to `NSPointerFunctionsCopyIn`.
> - `NSHashTableObjectPointerPersonality`: Use shifted pointer for the hash value and direct comparison to determine equality; use the description method for a description. Equal to `NSPointerFunctionsObjectPointerPersonality`.
## `NSMapTable`
@@ -70,35 +70,35 @@ NSLog(@"Members: %@", [hashTable allObjects]);
Instances where one might use `NSMapTable` include non-copyable keys and storing weak references to keyed delegates or another kind of weak object.
-~~~{swift}
+```swift
let delegate: AnyObject = ...
let mapTable = NSMapTable(keyOptions: .StrongMemory, valueOptions: .WeakMemory)
mapTable.setObject(delegate, forKey: "foo")
print("Keys: \(mapTable.keyEnumerator().allObjects)")
-~~~
-~~~{objective-c}
+```
+```objc
id delegate = ...;
NSMapTable *mapTable = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory
valueOptions:NSMapTableWeakMemory];
[mapTable setObject:delegate forKey:@"foo"];
NSLog(@"Keys: %@", [[mapTable keyEnumerator] allObjects]);
-~~~
+```
`NSMapTable` objects are initialized with options specifying behavior for both keys and values, using the following enum values:
> - `NSMapTableStrongMemory`: Specifies a strong reference from the map table to its contents.
> - `NSMapTableWeakMemory`: Uses weak read and write barriers appropriate for ARC or GC. Using `NSPointerFunctionsWeakMemory`, object references will turn to `NULL` on last release. Equal to `NSMapTableZeroingWeakMemory`.
> - `NSHashTableZeroingWeakMemory`: This option has been superseded by the `NSMapTableWeakMemory` option.
-> - `NSMapTableCopyIn`: Use the memory acquire function to allocate and copy items on input (see acquireFunction (see [`NSPointerFunction -acquireFunction`](http://developer.apple.com/library/ios/DOCUMENTATION/Cocoa/Reference/Foundation/Classes/NSPointerFunctions_Class/Introduction/Introduction.html#//apple_ref/occ/instp/NSPointerFunctions/acquireFunction)). Equal to NSPointerFunctionsCopyIn.
+> - `NSMapTableCopyIn`: Use the memory acquire function to allocate and copy items on input (see acquireFunction (see [`NSPointerFunction -acquireFunction`](https://developer.apple.com/library/ios/DOCUMENTATION/Cocoa/Reference/Foundation/Classes/NSPointerFunctions_Class/Introduction/Introduction.html#//apple_ref/occ/instp/NSPointerFunctions/acquireFunction)). Equal to NSPointerFunctionsCopyIn.
> - `NSMapTableObjectPointerPersonality`: Use shifted pointer hash and direct equality, object description.
Equal to `NSPointerFunctionsObjectPointerPersonality`.
### Subscripting
-`NSMapTable` doesn't implement [object subscripting](http://nshipster.com/object-subscripting/), but it can be trivially added in a category. `NSDictionary`'s `NSCopying` requirement for keys belongs to `NSDictionary` alone:
+`NSMapTable` doesn't implement [object subscripting](https://nshipster.com/object-subscripting/), but it can be trivially added in a category. `NSDictionary`'s `NSCopying` requirement for keys belongs to `NSDictionary` alone:
-~~~{swift}
+```swift
extension NSMapTable {
subscript(key: AnyObject) -> AnyObject? {
get {
@@ -114,9 +114,9 @@ extension NSMapTable {
}
}
}
-~~~
+```
-~~~{objective-c}
+```objc
@implementation NSMapTable (NSHipsterSubscripting)
- (id)objectForKeyedSubscript:(id)key
@@ -134,7 +134,7 @@ extension NSMapTable {
}
@end
-~~~
+```
---
diff --git a/2013-08-26-equality.md b/2013-08-26-equality.md
index 9dcb89b2..a03800ac 100644
--- a/2013-08-26-equality.md
+++ b/2013-08-26-equality.md
@@ -1,79 +1,111 @@
---
title: Equality
-author: Mattt Thompson
+author: Mattt
category: Objective-C
tags: nshipster
-excerpt: "The concept of equality is a central point of debate and inquiry in philosophy and mathematics, with far-reaching implications for matters of ethics, justice, and public policy. It is the task of programmers to reconcile our logical and physical understanding of equality with the semantic domains we model."
+excerpt: >
+ The concept of equality is a central topic in philosophy and mathematics,
+ with far-reaching implications for matters of
+ ethics, justice, and public policy.
+ Our task as programmers is to reconcile
+ our logical and physical understanding of equality
+ with the domains we model.
+revisions:
+ "2013-08-26": Original publication
+ "2018-08-15": Updated and expanded
status:
- swift: n/a
+ swift: n/a
---
-The concept of equality is a central point of debate and inquiry in philosophy and mathematics, with far-reaching implications for matters of ethics, justice, and public policy.
+The concept of equality is a central topic in philosophy and mathematics,
+with far-reaching implications for matters of ethics, justice, and public policy.
-From an empiricist perspective of the universe, two objects are equal if they are indistinguishable from one another in measurable observations. On a human scale, egalitarians hold that individuals should be considered equal members of the societal, economic, political, and judicial systems they inhabit.
+From an empiricist perspective of the universe,
+two objects are equal if they're indistinguishable by measurable observation.
+On a human scale,
+egalitarians hold that individuals should be considered equal members of the
+societal, economic, political, and judicial systems they inhabit.
-It is the task of programmers to reconcile our logical and physical understanding of equality with the semantic domains we model. There is a subtlety to the question of equality, too often overlooked. Jumping into implementation without sufficient understanding of semantics can lead to unnecessary work that produces incorrect results. Though an understanding of the mathematical and logical system underpinning is equally essential to making things work as modeled.
+Our task as programmers is to reconcile
+our logical and physical understanding of equality
+with the domains we model.
+And to do this correctly,
+we must start from a place of understanding.
-While the temptation for all technical blog posts is to skim for headings and code samples, please take a few minutes to read and understand all of this. **Copying relevant-looking code verbatim without knowing why its there may lead to incorrect behavior**. With all seriousness, equality is one of those topics—in Objective-C in particular—where there is still [a great deal of confusion](http://stackoverflow.com/questions/254281/best-practices-for-overriding-isequal-and-hash).
+So I invite you to take a moment to consider these broader questions;
+resist the urge to skim this article
+in search of relevant-looking code to copy-paste verbatim.
+Equality in Objective-C is a topic that remains a frequent source of confusion
+and deserves our full and undivided attention.
## Equality & Identity
-First and foremost, it is important to make a distinction between _equality_ and _identity_.
+First and foremost,
+let's make a distinction between
+equality and identity.
-Two objects may be _equal_ or _equivalent_ to one another, if they share a common set of observable properties. Yet, those two objects may still be thought to be _distinct_, each with their own _identity_. In programming, an object's identity is tied to its memory address.
+Two objects may be _equal_ or _equivalent_ to one another
+if they share a common set of observable properties.
+Yet those two objects may be thought to be _distinct_,
+each with their own _identity_.
-`NSObject` tests equality with another object with the method `isEqual:`. In its base implementation, an equality check is essentially a test for identity. Two `NSObject`s are considered equal if they point to the same memory address.
+In Objective-C, an object's identity is tied to its memory address.
+When you use the `==` operator to compare two objects in Objective-C,
+you're checking to see if they point to the same location in memory.
-~~~{objective-c}
-@implementation NSObject (Approximate)
-- (BOOL)isEqual:(id)object {
- return self == object;
-}
-@end
-~~~
+`NSObject` and its subclasses designate the `isEqual:` method
+to determine equality between two objects.
+In its base implementation,
+an equality check simply tests for equal identity:
-For container classes like `NSArray`, `NSDictionary`, and `NSString`, the expected and indeed more useful behavior would be to do a deep equality comparison, to test that each member in the collection is equal.
+```objc
+NSObject *a = [NSObject new];
+NSObject *b = [NSObject new];
-Subclasses of `NSObject` implementing their own `isEqual:` method are expected to do the following:
+BOOL objectsHaveSameIdentity = (a == b); // NO
+BOOL objectsAreEqual = ([a isEqual:b]); // NO
+```
-- Implement a new `isEqualTo__ClassName__:` method, which performs the meaningful value comparison.
-- Override `isEqual:` to make class and object identity checks, falling back on the aforementioned value comparison method.
-- Override `hash`, which will be described in the next section.
+However, some `NSObject` subclasses override `isEqual:`
+and thereby redefine the criteria for equality:
-Here's an idea of how `NSArray` might do this (ignoring, for this example, that as a [class cluster](https://developer.apple.com/library/ios/documentation/general/conceptual/CocoaEncyclopedia/ClassClusters/ClassClusters.html), the actual implementation would be significantly more complicated):
+An `NSValue` object is a wrapper around an underlying value.
+If you construct two `NSValue` objects from the same value,
+they'll return `NO` when compared with the `==` operator,
+but `YES` when compared using the `isEqual:` method:
-~~~{objective-c}
-@implementation NSArray (Approximate)
-- (BOOL)isEqualToArray:(NSArray *)array {
- if (!array || [self count] != [array count]) {
- return NO;
- }
+```objc
+NSPoint point = NSMakePoint(2.0, 3.0);
+NSValue *a = [NSValue valueWithPoint:point];
+NSValue *b = [NSValue valueWithPoint:point];
- for (NSUInteger idx = 0; idx < [array count]; idx++) {
- if (![self[idx] isEqual:array[idx]]) {
- return NO;
- }
- }
+BOOL valuesHaveSameIdentity = (a == b); // NO
+BOOL valuesAreEqual = ([a isEqual:b]); // YES
+```
- return YES;
-}
+`NSObject` and `NSValue` have different semantics for equality,
+and understanding the difference between them
+is the key to understanding how equality works in most programming languages.
-- (BOOL)isEqual:(id)object {
- if (self == object) {
- return YES;
- }
+### Value vs. Reference Semantics
- if (![object isKindOfClass:[NSArray class]]) {
- return NO;
- }
+If the most important thing about an object is its **state**,
+then it's known as a value type,
+and its observable properties are used to determine equality.
- return [self isEqualToArray:(NSArray *)object];
-}
-@end
-~~~
+If the most important thing about an object is its **identity**,
+then it's known as a reference type,
+and its memory address is used to determine equality.
+
+The naming of `NSValue` is therefore appropriate
+because objects of that type follow value semantics
+when determining equality in `isEqual:`.
-The following `NSObject` subclasses in Foundation have custom equality implementations, with the corresponding method:
+You'll find plenty of other value types throughout Foundation ---
+just look for their telltale `isEqualTo<#ClassName#>:` method.
+For example:
+- `NSArray -isEqualToArray:`
- `NSAttributedString -isEqualToAttributedString:`
- `NSData -isEqualToData:`
- `NSDate -isEqualToDate:`
@@ -85,141 +117,342 @@ The following `NSObject` subclasses in Foundation have custom equality implement
- `NSSet -isEqualToSet:`
- `NSString -isEqualToString:`
- `NSTimeZone -isEqualToTimeZone:`
-- `NSValue -isEqualToValue:`
-When comparing two instances of any of these classes, one is encouraged to use these high-level methods rather than `isEqual:`.
+> When comparing two instances of any of these classes,
+> use these high-level methods rather than `isEqual:`.
-However, our theoretical implementation is yet incomplete. Let's turn our attention now to `hash` (after a quick detour to clear something up about `NSString`:
+{% info %}
-### The Curious Case of `NSString` Equality
+The `isEqualTo<#ClassName#>:` methods *don't* accept `nil` as a parameter, whereas `isEqual:` does (and returns `NO` if passed `nil`). Also, watch out for the `-isEqualTo:` category method declared in `NSScriptWhoseTests.h`, which is unrelated despite its similar name.
-As an interesting aside, consider the following:
-
-~~~{objective-c}
-NSString *a = @"Hello";
-NSString *b = @"Hello";
-BOOL wtf = (a == b); // YES
-~~~
+{% endinfo %}
-Let it be perfectly clear that the correct way to compare `NSString` objects is to use `-isEqualToString:`. **Under no circumstances should you compare `NSString` with the `==` operator.**
+Types that encapsulate a single value,
+such as `NSDate`,
+perform an equality comparison of that value.
+In the case of `NSDate`,
+which represents a point in time relative to an absolute reference date
+(1 Jan 2001 00:00:00 GMT),
+objects are compared using their
+[offset value](https://developer.apple.com/documentation/corefoundation/cfabsolutetime?language=objc).
-So what's going on here? Why does this work, when the same code for `NSArray` or `NSDictionary` literals wouldn't work?
+For container classes like `NSArray` and `NSDictionary`,
+deep equality comparison is performed
+by checking that each member-wise pair in the collections are equal to each other.
+Here's an idea of how `NSArray` might implement `isEqualToArray:`,
+and how that relates to its implementation of `isEqual:`
+(ignoring for a moment that, as a
+[class cluster](https://developer.apple.com/library/ios/documentation/general/conceptual/CocoaEncyclopedia/ClassClusters/ClassClusters.html),
+the actual implementation would be significantly more complicated):
-It all has to do with an optimization technique known as [string interning](http://en.wikipedia.org/wiki/String_interning), whereby one copy of immutable string value is copied for each distinct value. `NSString *a` and `*b` point to the same copy of the interned string value `@"Hello"`. _Note that this only works for statically defined immutable strings._
+```objc
+@implementation NSArray // Simplified
+- (BOOL)isEqualToArray:(NSArray *)array {
+ if (!array || [self count] != [array count]) {
+ return NO;
+ }
-Interestingly enough, Objective-C selector names are also stored as interned strings in a shared string pool.
+ for (NSUInteger idx = 0; idx < [array count]; idx++) {
+ if (![self[idx] isEqual:array[idx]]) {
+ return NO;
+ }
+ }
-`themoreyouknow.gif`.
+ return YES;
+}
-## Hashing
+- (BOOL)isEqual:(nullable id)object {
+ if (object == nil) {
+ return NO;
+ }
-The primary use case of object equality tests for everyday object-oriented programming is to determine collection membership. To keep this fast, subclasses with custom equality implementations are expected to implement `hash` as well:
+ if (self == object) {
+ return YES;
+ }
-- Object equality is _commutative_ (`[a isEqual:b]` ⇒ `[b isEqual:a]`)
-- If objects are equal, then their `hash` values must also be equal (`[a isEqual:b]` ⇒ `[a hash] == [b hash]`)
-- However, the converse does not hold: two objects need not be equal in order for their hash values to be equal (`[a hash] == [b hash]` ¬⇒ `[a isEqual:b]`)
+ if (![object isKindOfClass:[NSArray class]]) {
+ return NO;
+ }
-Now for a quick flashback to Computer Science 101:
+ return [self isEqualToArray:(NSArray *)object];
+}
+@end
+```
----
+### String Interning
-A [hash table](http://en.wikipedia.org/wiki/Hash_table) is a fundamental data structure in programming, and it's what enables `NSSet` & `NSDictionary` to have fast (`O(1)`) lookup of elements.
+After learning about the differences between reference and value semantics,
+and how they change the behavior of `==` and `isEqual:`,
+you may be confused by the following behavior:
-We can best understand hash tables by contrasting them to arrays:
+```objc
+NSString *a = @"Hello";
+NSString *b = @"Hello";
-**Arrays** store elements in sequential indexes, such that an Array of size `n` will have slots at positions `0`, `1`, up to `n - 1`. To determine where an element is stored in the array (if at all), each position would have to be checked one-by-one (unless the array happens to be sorted, but that's another story).
+BOOL valuesHaveSameIdentity = (a == b); // YES (?)
+BOOL valuesAreEqual = ([a isEqual:b]); // YES
+```
+
+_What?_
+`NSString` is a value type,
+so why does `==` return `YES` for what should be two different objects?
+
+It all has to do with an optimization technique known as
+[string interning](https://en.wikipedia.org/wiki/String_interning),
+whereby one copy of immutable string value is copied for each distinct value.
+`NSString *a` and `NSString *b`
+point to the same copy of the interned string value `@"Hello"`.
+
+Objective-C selector names are also stored as interned strings in a shared pool.
+This is an important optimization
+for a language that operates by passing messages back and forth;
+being able to quickly check strings by pointer equality
+has a huge impact on runtime performance.
+
+> Note that this only works for statically-defined, immutable strings.
+
+### Tagged Pointers
+
+"Fair enough," you might say to yourself at this point.
+"Strings are important and complicated,
+so I understand why things may not work as I originally expected."
+
+Unfortunately,
+your understanding would be further confounded
+when `NSDate` doesn't work as you expect, either:
+
+```objc
+NSTimeInterval timeInterval = 556035120;
+NSDate *a = [NSDate dateWithTimeIntervalSinceReferenceDate:timeInterval];
+NSDate *b = [NSDate dateWithTimeIntervalSinceReferenceDate:timeInterval];
+
+BOOL valuesHaveSameIdentity = (a == b); // YES (?)
+BOOL valuesAreEqual = ([a isEqual:b]); // YES
+```
+
+_Seriously?_
+We spent all that time explaining the difference between `==` and `isEqual:`
+only to learn that it's all a lie?
+
+Well... kinda.
+Not so much a lie as an omission.
+
+What you're seeing here is another optimization technique at work,
+known as
+[pointer tagging](https://en.wikipedia.org/wiki/Tagged_pointer).
+
+The Objective-C runtime,
+when running in 64-bit mode,
+represents object pointers using 64-bit integers.
+Normally, this integer value points to an address in memory
+where the object is stored.
+But as an optimization,
+some small values can be stored directly in the pointer itself.
+If the least-significant bit is set to `1`,
+a pointer is considered to be tagged;
+the runtime reads the next 3 bits to determine the tagged class
+and then initializes a value of that class using the next 60 bits.
+
+If we run the `NSDate` comparison code again with the debugger turned on,
+we can confirm that `a` and `b` are both instances of `__NSTaggedDate *`
+with odd pointer values (i.e. their least-significant digit is `1`).
+
+> As an interesting tie-in to our previous section,
+> `NSString` gained support for tagged pointers in macOS 10.10 & iOS 8.
+> [Mike Ash](https://github.com/mikeash)
+> has [a fascinating write-up](https://mikeash.com/pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html)
+> of how that works.
+
+Only a handful of Foundation types implement tagged pointers,
+so don't expect your own objects to magically get this behavior.
-**Hash Tables** take a slightly different approach. Rather than storing elements sequentially (`0`, `1`, `...`, `n-1`), a hash table allocates `n` positions in memory, and uses a function to calculate a position within that range. A hash function is [deterministic](http://en.wikipedia.org/wiki/Deterministic_algorithm), and a _good_ hash function generates values in a relatively [uniform distribution](http://en.wikipedia.org/wiki/Uniform_distribution_%28discrete%29) without being too computationally expensive. A _hash collision_ occurs when two different objects calculate the same hash value. When this happens, the hash table will seek from the point of collision and place the new object in the first available place. As a hash table becomes more congested, the likelihood of collision increases, which leads to more time spent looking for a free space (hence why a hash function with a uniform distribution is so desireable).
+## Hashing
----
+One of the most important applications of object equality
+is to determine collection membership.
+In order to keep this fast for `NSDictionary` and `NSSet` collections,
+subclasses with custom equality implementations
+are expected to implement the `hash` method
+in a way that satisfies the following criteria:
+
+- Object equality is _commutative_
+ (`[a isEqual:b]` ⇒ `[b isEqual:a]`)
+- If objects are equal,
+ then their `hash` values must also be equal
+ (`[a isEqual:b]` ⇒ `[a hash] == [b hash]`)
+- However, the converse does not hold:
+ two objects can have the same hash values,
+ but not be equal to one another
+ (`[a hash] == [b hash]` ¬⇒ `[a isEqual:b]`)
-One of the most common misconceptions about implementing a custom `hash` function comes from [affirming the consequent](http://en.wikipedia.org/wiki/Affirming_the_consequent), thinking that `hash` values _must_ be distinct. This often leads to [needlessly complicated implementations involving the magical incantation of prime numbers copied from Java textbooks](http://stackoverflow.com/a/254380/157142). In reality, a simple [`XOR`](http://en.wikipedia.org/wiki/Exclusive_or) over the hash values of critical properties is sufficient 99% of the time.
+Now for a quick flashback to Computer Science 101:
-The trick is in thinking about what the critical value of an object is.
+---
-For an `NSDate`, the time interval since a reference date would be sufficient:
+A [hash table](https://en.wikipedia.org/wiki/Hash_table)
+is a fundamental data structure in programming.
+
+We can best understand hash tables by contrasting them to lists:
+
+**Lists** store elements sequentially.
+If you want to see whether a particular object is contained by a list,
+you must check each element in the list sequentially
+until you either find what you're looking for or run out of items.
+Therefore, the amount of time it takes to perform a lookup
+has a linear relationship to the number of elements in the list (`O(n)`).
+`NSArray` is the primary list type in Foundation.
+
+**Hash tables** take a slightly different approach.
+Rather than storing elements sequentially,
+a hash table allocates a fixed number of positions in memory
+and uses a function to calculate the position within that range
+for each object when it's inserted.
+A hash function is
+[deterministic](https://en.wikipedia.org/wiki/Deterministic_algorithm),
+and a _good_ hash function generates values in a relatively
+[uniform distribution](https://en.wikipedia.org/wiki/Uniform_distribution_%28discrete%29)
+without being too computationally expensive.
+Ideally, the amount of time it takes
+to find an element in a hash table is constant (`O(1)`),
+independent of how many elements are stored.
+`NSSet` and `NSDictionary` are the primary collections in Foundation
+that implement hash tables.
+
+> There is one important caveat to these performance characteristics, though:
+> If two different objects
+> produce the same hash value,
+> the hash table seeks from the calculated index
+> and places the new object in the first available spot.
+> We call this a hash collision.
+> As a hash table becomes more congested,
+> the likelihood of collision increases,
+> which leads to more time spent looking for a free space
+> (hence why a hash function with a uniform distribution is so desirable).
+
+## Best Practices when Implementing Value Types
+
+If you're implementing a custom type
+and want it to follow value semantics,
+do the following:
+
+- Implement a new `isEqualTo<#ClassName#>:` method
+ to test for value equality.
+- Override the `isEqual:` method,
+ starting with early nil check and class and object identity checks
+ and falling back on the aforementioned value equality test.
+- Override the `hash` method such that equal objects
+ produce the same hash value.
+
+As an example,
+consider the following `Color` type,
+which represents a color using floating-point values for
+red, green, and blue intensities between `0` and `1`:
+
+```objc
+@interface Color: NSObject
+@property NSNumber *red;
+@property NSNumber *green;
+@property NSNumber *blue;
+@end
+```
-~~~{objective-c}
-@implementation NSDate (Approximate)
-- (NSUInteger)hash {
- return (NSUInteger)abs([self timeIntervalSinceReferenceDate]);
-}
-~~~
+### Implementing `isEqualTo<#ClassName#>:`
-For a `UIColor`, a bit-shifted sum of RGB components is a convenient calculation:
+The `isEqualTo<#ClassName#>:` method should be publicly declared
+and provide a test for value equality with another object of the same type.
-~~~{objective-c}
-@implementation UIColor (Approximate)
-- (NSUInteger)hash {
- CGFloat red, green, blue;
- [self getRed:&red green:&green blue:&blue alpha:nil];
- return ((NSUInteger)(red * 255) << 16) + ((NSUInteger)(green * 255) << 8) + (NSUInteger)(blue * 255);
+```objc
+- (BOOL)isEqualToColor:(Color *)color {
+ return [self.red isEqualToNumber:color.red] &&
+ [self.green isEqualToNumber:color.green] &&
+ [self.blue isEqualToNumber:color.blue];
}
-@end
-~~~
+```
-## Implementing `-isEqual:` and `hash` in a Subclass
+Implementations of this method typically perform member-wise comparison
+between the receiver and the passed argument
+for each of the properties of that type.
+In the case of a `Color`, that means checking the
+`red`, `green` , and `blue` properties of each color for equality.
-Bringing it all together, here's how one might override the default equality implementation in a subclass:
+> Be sure to use the corresponding value equality method
+> for each of the properties.
-~~~{objective-c}
-@interface Person
-@property NSString *name;
-@property NSDate *birthday;
+### Implementing `isEqual:`
-- (BOOL)isEqualToPerson:(Person *)person;
-@end
+The `isEqual:` method should delegate to
+the `isEqualTo<#ClassName#>:` method
+after testing for nil argument, pointer equality,
+and checking for type identity:
-@implementation Person
+```objc
+- (BOOL)isEqual:(nullable id)object {
+ if (object == nil) {
+ return NO;
+ }
-- (BOOL)isEqualToPerson:(Person *)person {
- if (!person) {
- return NO;
- }
+ if (self == object) {
+ return YES;
+ }
- BOOL haveEqualNames = (!self.name && !person.name) || [self.name isEqualToString:person.name];
- BOOL haveEqualBirthdays = (!self.birthday && !person.birthday) || [self.birthday isEqualToDate:person.birthday];
+ if (![object isKindOfClass:[Color class]]) {
+ return NO;
+ }
- return haveEqualNames && haveEqualBirthdays;
+ return [self isEqualToColor:(Color *)object];
}
+```
-#pragma mark - NSObject
+### Implementing `hash`
-- (BOOL)isEqual:(id)object {
- if (self == object) {
- return YES;
- }
+A common misconception about custom `hash` implementations comes from
+[affirming the consequent](https://en.wikipedia.org/wiki/Affirming_the_consequent):
+thinking that `hash` values _must_ be distinct.
+Although an ideal hash function would produce all distinct values,
+this is significantly more difficult than what's required ---
+which is, if you'll recall:
- if (![object isKindOfClass:[Person class]]) {
- return NO;
- }
+> - Override the `hash` method such that equal objects
+> produce the same hash value.
- return [self isEqualToPerson:(Person *)object];
-}
+A simple way to satisfy this requirement is to simply
+[`XOR`](https://en.wikipedia.org/wiki/Exclusive_or)
+over the hash values of the properties that determine equality.
+```objc
- (NSUInteger)hash {
- return [self.name hash] ^ [self.birthday hash];
+ return [self.red hash] ^ [self.green hash] ^ [self.blue hash];
}
-~~~
-
-> For the curious and pedantic, see [this post from Mike Ash](http://www.mikeash.com/pyblog/friday-qa-2010-06-18-implementing-equality-and-hashing.html) for an explanation of how `hash` implementations might be improved by bit-shifting or rotating composite values that may overlap.
-
-## Don't Overthink It
-
-While all of this has been an interesting exercise in epistemology and computer science, there is a lingering pragmatic detail:
-
-**You don't usually need to implement this.**
-
-There are many situations where the default identity check (two variables point to the same address in memory) is desirable behavior. This comes as a consequence of the limitations of data modeling.
-
-Take, for instance, the previous example of the `Person` class. It's not inconceivable that two individuals would share a common name _and_ birthday. In reality, this crisis of identity would be resolved by additional information, whether it's a system-dependent identifier like a Social Security Number, their parents' identities, or any other physical attributes.
-
-> Yet even that additional information is not entirely foolproof. After all, that person could be cloned, teleported, or whisked away into a parallel universe. Unlikely? Sure. But much of the challenge in modeling systems is dealing with imperfect assumptions. Just saying.
-
-Ultimately, it's up to the abstraction to isolate the significant, identifying features that the system cares about, and disregard the rest. The developer can then decide whether objects will be used in such a way that set membership calculations should care about. In a program that only records `name` and `birthday`, it may perfectly correct to treat congruent instances as distinct entities.
+```
+
+Yes, this approach results in collisions
+for objects with the same values for different properties
+(for example, cyan and yellow produce the same hash value,
+because each has color channels with intensity equal to `1`).
+However, it may be good enough for what you're doing.
+
+Unless you have reason to believe that a better `hash` implementation
+would improve performance in a meaningful way,
+you're probably better off focusing your time elsewhere.
+(That's not to say that _all_ optimizations are premature,
+but rather that complicated hash functions frequently are).
+
+For the curious and pedantic,
+Mike Ash has
+[another blog post](http://www.mikeash.com/pyblog/friday-qa-2010-06-18-implementing-equality-and-hashing.html)
+with suggestions for improving hash functions using techniques like
+bit-shifting and rotating composite values that may overlap.
---
-Hopefully, after all of this explanation, we all stand with equal footing on this slippery subject.
-
-As humans, we strive to understand and implement equality in our society and economy; in the laws and leaders that govern us; in the understanding that we extend to one another as we journey through existence. May we continue towards that ideal, where individuals are judged by the contents of their character, just as we judge a variable by the contents of its memory address.
+Hopefully, after all of this explanation,
+we can all stand with an equal footing on this slippery subject.
+
+As humans,
+we strive to understand and implement equality in our society and economy;
+in the laws and leaders that govern us, and
+in the understanding that we extend to one another
+as we journey through existence.
+May we continue towards that ideal,
+where individuals are judged by the contents of their character,
+just as we judge a variable by the contents of its memory address.
diff --git a/2013-09-02-xcode-snippets.md b/2013-09-02-xcode-snippets.md
index 345be52f..844141bd 100644
--- a/2013-09-02-xcode-snippets.md
+++ b/2013-09-02-xcode-snippets.md
@@ -1,6 +1,6 @@
---
title: Xcode Snippets
-author: Mattt Thompson
+author: Mattt
category: Xcode
excerpt: "iOS development all but requires the use of Xcode. And if we're resigned to use an IDE in our development workflow, we might as well make the most of it, right? So this week on NSHipster, we're going to talk about one of the more powerful yet underused features of Xcode: Code Snippets"
status:
@@ -9,7 +9,7 @@ status:
iOS development all but requires the use of Xcode. To its credit, Xcode has improved pretty consistently over the last couple of years. Sure, [it still has its... quirks](http://www.textfromxcode.com), but hey—things could be [much, much worse](http://www.eclipse.org).
-Working in an IDE may not be as cool as working in your favorite [decades-old editor](http://en.wikipedia.org/wiki/Vim_(text_editor)) (or [that other one](http://en.wikipedia.org/wiki/Emacs)), but you know what is cool? [Autocompletion](http://www.textfromxcode.com/post/24542673087). Not to mention [Build & Analyze](http://clang-analyzer.llvm.org/xcode.html), [Breakpoints](https://developer.apple.com/library/ios/recipes/xcode_help-source_editor/Creating,Disabling,andDeletingBreakpoints/Creating,Disabling,andDeletingBreakpoints.html), and [Instruments](https://developer.apple.com/library/ios/DOCUMENTATION/DeveloperTools/Conceptual/InstrumentsUserGuide/InstrumentsQuickStart/InstrumentsQuickStart.html).
+Working in an IDE may not be as cool as working in your favorite [decades-old editor](https://en.wikipedia.org/wiki/Vim_(text_editor)) (or [that other one](https://en.wikipedia.org/wiki/Emacs)), but you know what is cool? [Autocompletion](http://www.textfromxcode.com/post/24542673087). Not to mention [Build & Analyze](http://clang-analyzer.llvm.org/xcode.html), [Breakpoints](https://developer.apple.com/library/ios/recipes/xcode_help-source_editor/Creating,Disabling,andDeletingBreakpoints/Creating,Disabling,andDeletingBreakpoints.html), and [Instruments](https://developer.apple.com/library/ios/DOCUMENTATION/DeveloperTools/Conceptual/InstrumentsUserGuide/InstrumentsQuickStart/InstrumentsQuickStart.html).
This is all to say: if we're resigned to use an IDE in our development workflow, we might as well make the most of it, right? So this week on NSHipster, we're going to talk about one of the more powerful yet underused features of Xcode: **Code Snippets**.
@@ -21,21 +21,21 @@ From `@interface` declarations to `if (!self) return nil;` incantations, there i
To see the available code snippets, show the Utilities panel, to the right of your editor. On the bottom half the Utilities panel, there will be a horizontal divider with 4 icons.
-
+
Click the `{ }` icon to show the Code Snippets Library.
-
+
There are two ways to insert a snippet into your code:
You can drag and drop from the code snippets library into your editor:
-
+
...or for snippets that include a text completion shortcut, you can start typing that:
-
+
To get a sense of what you can do with snippets, here's an overview of the ones built-in to Xcode:
@@ -46,7 +46,7 @@ To get a sense of what you can do with snippets, here's an overview of the ones
- Objective-C declarations for `@interface` (including for class extensions and categories), `@implementation`, `@protocol`
- Objective-C boilerplate for KVO, including the relatively obscure `keyPathsForValuesAffecting`, used for [registering dependent keys](https://developer.apple.com/library/ios/DOCUMENTATION/Cocoa/Conceptual/KeyValueObserving/Articles/KVODependentKeys.html)
- Objective-C boilerplate for Core Data fetches, property accessors, and property validation
-- Objective-C idioms for enumerating [`NSIndexSet`](http://nshipster.com/nsindexset/)
+- Objective-C idioms for enumerating [`NSIndexSet`](https://nshipster.com/nsindexset/)
- Objective-C incantation for `init`, `initWithCoder:` and `initWithFrame:` method implementations
- Objective-C `@try` / `@catch` / `@finally` and `@autorelease` blocks
- GCD idioms for `dispatch_once` and `dispatch_after`
@@ -57,11 +57,11 @@ Of course, what really makes snippets such a powerful feature is the ability to
The process of creating a snippet is actually pretty unintuitive and difficult to explain. It uses an obscure OS X system feature that allows users to create a "Text Clipping" by dragging and dropping selected text. Much easier to just show it in action:
-
+
After being added to the code snippet library, a user-defined snippet can be edited by double-clicking its listing:
-
+
Each snippet has the following fields:
@@ -87,7 +87,7 @@ Each snippet has the following fields:
Something you may have noticed in using other Xcode snippets are placeholder tokens:
-
+
In Xcode, placeholder tokens are delimited by `<#` and `#>`, with the placeholder text in the middle. Go ahead—try typing that into Xcode, and watch as the text between the octothorp tags magically transforms right in front of your eyes.
diff --git a/2013-09-09-network-link-conditioner.md b/2013-09-09-network-link-conditioner.md
index a0e01410..f5bc26cd 100644
--- a/2013-09-09-network-link-conditioner.md
+++ b/2013-09-09-network-link-conditioner.md
@@ -1,70 +1,178 @@
---
title: Network Link Conditioner
-author: Mattt Thompson
+author: Mattt
category: Xcode
tag: popular
-excerpt: "Product design is about empathy. Knowing what a user wants, what they like, what they dislike, what causes them frustration, and learning to understand and embody those motivations in design decisions—this is what it takes to make something insanely great."
+excerpt: >-
+ App developers often forget to test how their apps perform
+ under less-than-ideal networking environments.
+ Learn how you can use the Network Link conditioner
+ to simulate a spotty Internet connection on your device.
+revisions:
+ "2018-07-18": Updated for Xcode 10
+ "2019-07-29": Added note about installation problems in macOS 10.14
status:
- swift: n/a
+ swift: n/a
---
-Product design is about empathy. Knowing what a user wants, what they like, what they dislike, what causes them frustration, and learning to understand and embody those motivations in design decisions—this is what it takes to make something insanely great.
+Product design is about empathy.
+Knowing what a user wants,
+what they like,
+what they dislike,
+what causes them frustration,
+and learning to understand and embody those motivations ---
+this is what it takes to make something insanely great.
+
+And so we invest in reaching beyond our own operational model of the world.
+We tailor our experience to
+[different locales](/nslocalizedstring/).
+We consider the usability implications of
+[screen readers or other assistive technologies](/uiaccessibility/).
+We [continuously evaluate](/unit-testing/)
+our implementation against these expectations.
+
+There is, however,
+one critical factor that app developers often miss:
+**network condition**,
+or more specifically,
+the latency and bandwidth of an Internet connection.
+
+For something so essential to user experience,
+it's unfortunate that most developers take an ad-hoc approach
+to field-testing their apps under different conditions
+(if at all).
+
+This week on NSHipster,
+we'll be talking about the
+[Network Link Conditioner](https://developer.apple.com/download/more/?q=Additional%20Tools),
+a utility that allows macOS and iOS devices
+to accurately and consistently simulate adverse networking environments.
-And so we invest in reaching beyond our own operational model of the world. We tailor our experience for [different locales](http://nshipster.com/nslocalizedstring/). We consider the usability implications of [screen readers or other assistive technologiess](http://nshipster.com/uiaccessibility/). We [continuously evaluate](http://nshipster.com/unit-testing/) our implementation against these expectations.
+## Installation
-There is, though, one critical factor that app developers often miss the first time around, and that is **network condition**, or more specifically the latency and bandwidth of an Internet connection. For something so essential to a user's experience with a product, it's unfortunate that most developers take an ad-hoc approach to field testing different kinds of environments, if at all.
+Network Link Conditioner can be found
+in the "Additional Tools for Xcode" package.
+You can download this from the
+[Downloads for Apple Developers](https://developer.apple.com/download/more/?q=Additional%20Tools)
+page.
-This week on NSHipster, we'll be talking about the [Network Link Conditioner](https://developer.apple.com/downloads/index.action?q=Hardware%20IO%20Tools), a utility that allows Mac and iOS devices to accurately and consistently simulate adverse networking environments.
+Search for "Additional Tools"
+and select the appropriate release of the package.
-## Installation
+
+
+
+
-Network Link Conditioner can be found in the "Hardware IO Tools for Xcode" package. This can be downloaded from the [Apple Developer Downloads](https://developer.apple.com/downloads/index.action?q=Hardware%20IO%20Tools) page.
+Once the download has finished,
+open the DMG,
+navigate to the "Hardware" directory,
+and double-click "Network Link Condition.prefPane".
-
+
+
+
+
-Search for "Hardware IO Tools for Xcode", and select the appropriate release of the package.
+Click on the Network Link Conditioner preference pane
+at the bottom of System Preferences.
-
+
+ {% comment %}{% endcomment %}
+
+
-Once the download has finished, open the DMG and double-click "Network Link Condition.prefPane" to install.
+{% error %}
-
+When you first install Network Link Conditioner on macOS 10.14,
+everything works as expected.
+But if you close and reopen System Preferences,
+the preference pane no longer appears,
+and attempting to reinstall results in the following error message:
-From now on, you can enable the Network Link Conditioner from its preference pane at the bottom of System Preferences.
+
-
+
+
+
+
-When enabled, the Network Link Conditioner can change the network environment of the iPhone Simulator according to one of the built-in presets:
+
-- EDGE
+> **You can’t install the “Network Link Conditioner” preferences.**
+> “Network Link Conditioner” preferences is installed with macOS and can’t be replaced.
+
+
+
+
+As a workaround,
+you can move the preference pane
+from your user `PreferencePanes` directory to the system-level directory
+by entering the following command in `Terminal.app`
+(you'll be prompted for your password):
+
+```terminal
+$ sudo mv ~/Library/PreferencePanes/Network\ Link\ Conditioner.prefPane /Library/PreferencePanes/
+```
+
+Once you've done this,
+Network Link Conditioner will appear
+the next time you open System Preferences.
+
+{% enderror %}
+
+## Controlling Bandwidth, Latency, and Packet Loss
+
+Enabling the Network Link Conditioner
+changes the network environment system-wide
+according to the selected configuration,
+limiting uplink or download
+[bandwidth](https://en.wikipedia.org/wiki/Bandwidth_%28computing%29),
+[latency](https://en.wikipedia.org/wiki/Latency_%28engineering%29%23Communication_latency), and rate of
+[packet loss](https://en.wikipedia.org/wiki/Packet_loss).
+
+You can choose from one of the following presets:
+
+- 100% Loss
- 3G
- DSL
-- WiFi
+- EDGE
- High Latency DNS
+- LTE
- Very Bad Network
-- 100% Loss
+- WiFi
+- WiFi 802.11ac
-Each preset can set a limit for uplink or downlink [bandwidth](http://en.wikipedia.org/wiki/Bandwidth_%28computing%29), [latency](http://en.wikipedia.org/wiki/Latency_%28engineering%29%23Communication_latency), and rate of [packet loss](http://en.wikipedia.org/wiki/Packet_loss) (when any value is set to 0, that value is unchanged from your computer's network environment).
+...or create your own according to your particular requirements.
-
+
-You can also create your own preset, if you wish to simulate a particular combination of factors simultaneously.
+---
-Try running your app in the simulator with the Network Link Conditioner enabled under various presets and see what happens. How does network latency affect your app startup? What effect does bandwidth have on table view scroll performance? Does your app work at all with 100% packet loss?
+Now try running your app with the Network Link Conditioner enabled:
-> If your app uses [Reachability](https://developer.apple.com/library/ios/samplecode/Reachability/Introduction/Intro.html) to detect network availability, you may experience some unexpected results while using the Network Link Conditioner. As such, any reachability behavior under Airplane mode or WWan / WiFi distinctions is something that should be tested separately from network conditioning.
+How does network latency affect your app startup?
+What effect does bandwidth have on table view scroll performance?
+Does your app work at all with 100% packet loss?
## Enabling Network Link Conditioner on iOS Devices
-While the preference pane works well for developing on the simulator, it's also important to test on actual devices. Fortunately, as of iOS 6, the Network Link Conditioner is available on the devices themselves.
+Although the preference pane works well for developing on the simulator,
+it's also important to test on a real device.
+Fortunately,
+the Network Link Conditioner is available for iOS as well.
-To enable it, you need to set up your device for development:
+To use the Network Link Conditioner on iOS,
+set up your device for development:
-1. Connect your iPhone or iPad to your Mac
-2. In Xcode, go to Window > Organizer (⇧⌘2)
-3. Select your device in the sidebar
-4. Click "Use for Development"
+1. Connect your iOS device to your Mac
+2. In Xcode, navigate to Window > Devices & Simulators
+3. Select your device in the sidebar
+4. Click "Use for Development"
-
+
-Now you'll have access to the Developer section of the Settings app, where you'll find the Network Link Conditioner (just don't forget to turn it off after you're done testing!).
+Now you'll have access to the Developer section of the Settings app.
+You can enable and configure the Network Link Conditioner
+on your iOS device under Settings > Developer > Networking.
+(Just remember to turn it off after you're done testing!).
diff --git a/2013-09-16-afnetworking-2.md b/2013-09-16-afnetworking-2.md
index 34048ae9..5cf16dee 100644
--- a/2013-09-16-afnetworking-2.md
+++ b/2013-09-16-afnetworking-2.md
@@ -1,10 +1,11 @@
---
title: AFNetworking 2.0
-author: Mattt Thompson
+author: Mattt
category: Open Source
excerpt: "AFNetworking is one of the most widely used open source projects for iOS and OS X development. It's about as mainstream as it gets. But have you heard about the sequel?"
+retired: true
status:
- swift: n/a
+ swift: n/a
---
[AFNetworking](http://afnetworking.com) is one of the most widely used open source projects for iOS and OS X development. It powers thousands of popular and critically acclaimed apps, and serves as the foundation for dozens of other great open source libraries and frameworks. With thousands of stars and forks, and hundreds of contributors, the project is also among the most active and influential in the community.
@@ -20,13 +21,13 @@ This week on NSHipster: an exclusive look at the future of AFNetworking.
## AFNetworking's Big Ideas
-Started in May 2011 as a humble extension of an [Apple code sample](https://developer.apple.com/library/ios/samplecode/mvcnetworking/Introduction/Intro.html) from a [doomed location-based social network](http://en.wikipedia.org/wiki/Gowalla), AFNetworking's success was a product of timing more than anything. At a time when [ASIHTTPRequest](https://github.com/pokeb/asi-http-request) was the de facto networking solution, AFNetworking's core ideas caught on among developers looking for a more modern solution.
+Started in May 2011 as a humble extension of an [Apple code sample](https://developer.apple.com/library/ios/samplecode/mvcnetworking/Introduction/Intro.html) from a [doomed location-based social network](https://en.wikipedia.org/wiki/Gowalla), AFNetworking's success was a product of timing more than anything. At a time when [ASIHTTPRequest](https://github.com/pokeb/asi-http-request) was the de facto networking solution, AFNetworking's core ideas caught on among developers looking for a more modern solution.
### NSURLConnection + NSOperation
`NSURLConnection` is the backbone of the Foundation URL Loading system. An `NSURLConnection` loads an `NSURLRequest` object asynchronously, calling methods on its delegates as the `NSURLResponse` / `NSHTTPURLResponse` and its associated `NSData` is sent to and loaded from the server; the delegate may also implement behavior for handling an `NSURLAuthenticationChallenge`, a redirect responses, or determine how the associated `NSCachedURLResponse` is stored to the shared `NSURLCache`.
-[`NSOperation`](http://nshipster.com/nsoperation) is an abstract class that models a single unit of computation, with useful constructs like state, priority, dependencies, and cancellation.
+[`NSOperation`](https://nshipster.com/nsoperation) is an abstract class that models a single unit of computation, with useful constructs like state, priority, dependencies, and cancellation.
The first major breakthrough of AFNetworking was combining the two. `AFURLConnectionOperation`, an `NSOperation` subclass conforms to `NSURLConnectionDelegate` methods, and tracks the state of the request from start to finish, while storing intermediary state, such as request, response, and response data.
@@ -52,8 +53,8 @@ With its second major release, AFNetworking aims to reconcile many of the quirks
### Motivations
-- **NSURLSession Compatibility** - `NSURLSession` is a replacement for `NSURLConnection` introduced in iOS 7. `NSURLConnection` isn't deprecated, and likely won't be for some time, but `NSURLSession` is the future of networking in Foundation, and it's a bright future at that, addressing many of the shortcomings of its predecessor. (See WWDC 2013 Session 705 "What’s New in Foundation Networking" for a good overview). Some had initially speculated that `NSURLSession` would obviate the need for AFNetworking; although there is overlap, there is still much that a higher-level abstraction can provide. __AFNetworking 2.0 does just this, embracing and extending `NSURLSession` to pave over some of the rough spots, and maximize its usefulness.__
-- **Modularity** - One of the major criticisms of AFNetworking is how bulky it is. Although its architecture lent itself well to modularity on a class level, its packaging didn't allow for individual features to be selected à la carte. Over time, `AFHTTPClient` in particular became overburdened in its responsibilities (creating requests, serializing query string parameters, determining response parsing behavior, creating and managing operations, monitoring network reachability). __In AFNetworking 2.0, you can pick and choose only the components you need using [CocoaPods subspecs](https://github.com/CocoaPods/CocoaPods/wiki/The-podspec-format#subspecs).__
+- **NSURLSession Compatibility** - `NSURLSession` is a replacement for `NSURLConnection` introduced in iOS 7. `NSURLConnection` isn't deprecated, and likely won't be for some time, but `NSURLSession` is the future of networking in Foundation, and it's a bright future at that, addressing many of the shortcomings of its predecessor. (See WWDC 2013 Session 705 "What’s New in Foundation Networking" for a good overview). Some had initially speculated that `NSURLSession` would obviate the need for AFNetworking; although there is overlap, there is still much that a higher-level abstraction can provide. **AFNetworking 2.0 does just this, embracing and extending `NSURLSession` to pave over some of the rough spots, and maximize its usefulness.**
+- **Modularity** - One of the major criticisms of AFNetworking is how bulky it is. Although its architecture lent itself well to modularity on a class level, its packaging didn't allow for individual features to be selected à la carte. Over time, `AFHTTPClient` in particular became overburdened in its responsibilities (creating requests, serializing query string parameters, determining response parsing behavior, creating and managing operations, monitoring network reachability). **In AFNetworking 2.0, you can pick and choose only the components you need using [CocoaPods subspecs](https://github.com/CocoaPods/CocoaPods/wiki/The-podspec-format#subspecs).**
### Meet the Cast
@@ -65,7 +66,7 @@ With its second major release, AFNetworking aims to reconcile many of the quirks
#### `NSURLSession` Components _(iOS 7)_
-- `AFURLSessionManager` - A class that creates and manages an `NSURLSession` object based on a specified `NSURLSessionConfiguration` object, as well as data, download, and upload tasks for that session, implementing the delegate methods for both the session and its associated tasks. Because of the odd gaps in `NSURLSession`'s API design, __any code working with `NSURLSession` would be improved by `AFURLSessionManager`__.
+- `AFURLSessionManager` - A class that creates and manages an `NSURLSession` object based on a specified `NSURLSessionConfiguration` object, as well as data, download, and upload tasks for that session, implementing the delegate methods for both the session and its associated tasks. Because of the odd gaps in `NSURLSession`'s API design, **any code working with `NSURLSession` would be improved by `AFURLSessionManager`**.
- `AFHTTPSessionManager` - A subclass of `AFURLSessionManager` that encapsulates the common patterns of communicating with an web service over HTTP, backed by `NSURLSession` by way of `AFURLSessionManager`.
---
@@ -87,7 +88,7 @@ One of the breakthroughs of AFNetworking 2.0's new architecture is use of serial
Thanks to the contributions of [Dustin Barker](https://github.com/dstnbrkr), [Oliver Letterer](https://github.com/OliverLetterer), and [Kevin Harwood](https://github.com/kcharwood) and others, AFNetworking comes with built-in support for [SSL pinning](http://blog.lumberlabs.com/2012/04/why-app-developers-should-care-about.html), which is critical for apps that deal with sensitive information.
-- `AFSecurityPolicy` - A class that evaluates the server trust of secure connections against its specified pinned certificates or public keys. tl;dr Add your server certificate to your app bundle to help prevent against [man-in-the-middle attacks](http://en.wikipedia.org/wiki/Man-in-the-middle_attack).
+- `AFSecurityPolicy` - A class that evaluates the server trust of secure connections against its specified pinned certificates or public keys. tl;dr Add your server certificate to your app bundle to help prevent against [man-in-the-middle attacks](https://en.wikipedia.org/wiki/Man-in-the-middle_attack).
#### Reachability
@@ -97,9 +98,9 @@ Another piece of functionality now decoupled from `AFHTTPClient` is network reac
#### Real-time
-- `AFEventSource` - An Objective-C implementation of the [`EventSource` DOM API](http://en.wikipedia.org/wiki/Server-sent_events). A persistent HTTP connection is opened to a host, which streams events to the event source, to be dispatched to listeners. Messages streamed to the event source formatted as [JSON Patch](http://tools.ietf.org/html/rfc6902) documents are translated into arrays of `AFJSONPatchOperation` objects. These patch operations can be applied to the persistent data set fetched from the server.
+- `AFEventSource` - An Objective-C implementation of the [`EventSource` DOM API](https://en.wikipedia.org/wiki/Server-sent_events). A persistent HTTP connection is opened to a host, which streams events to the event source, to be dispatched to listeners. Messages streamed to the event source formatted as [JSON Patch](http://tools.ietf.org/html/rfc6902) documents are translated into arrays of `AFJSONPatchOperation` objects. These patch operations can be applied to the persistent data set fetched from the server.
-~~~{objective-c}
+```objc
NSURL *URL = [NSURL URLWithString:@"http://example.com"];
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:URL];
[manager GET:@"/resources" parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
@@ -117,7 +118,7 @@ AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:UR
}
} error:nil];
} failure:nil];
-~~~
+```
#### UIKit Extensions
@@ -125,10 +126,10 @@ All of the UIKit categories in AFNetworking 2.0 have been extracted and expanded
- `AFNetworkActivityIndicatorManager`: Automatically start and stop the network activity indicator in the status bar as request operations and tasks begin and finish loading.
- `UIImageView+AFNetworking`: Adds `imageResponseSerializer` property, which makes it easy to automatically resize or apply a filter to images loaded remotely to an image view. For example, [`AFCoreImageSerializer`](https://github.com/AFNetworking/AFCoreImageSerializer) could be used to apply Core Image filters to the response image before being displayed.
-- `UIButton+AFNetworking` *(New)*: Similar to `UIImageView+AFNetworking`, loads `image` and `backgroundImage` from remote source.
-- `UIActivityIndicatorView+AFNetworking` *(New)*: Automatically start and stop a `UIActivityIndicatorView` according to the state of a specified request operation or session task.
-- `UIProgressView+AFNetworking` *(New)*: Automatically track the upload or download progress of a specified request operation or session task.
-- `UIWebView+AFNetworking` *(New)*: Provides a more sophisticated API for loading URL requests, with support for progress callbacks and content transformation.
+- `UIButton+AFNetworking` _(New)_: Similar to `UIImageView+AFNetworking`, loads `image` and `backgroundImage` from remote source.
+- `UIActivityIndicatorView+AFNetworking` _(New)_: Automatically start and stop a `UIActivityIndicatorView` according to the state of a specified request operation or session task.
+- `UIProgressView+AFNetworking` _(New)_: Automatically track the upload or download progress of a specified request operation or session task.
+- `UIWebView+AFNetworking` _(New)_: Provides a more sophisticated API for loading URL requests, with support for progress callbacks and content transformation.
---
@@ -138,9 +139,9 @@ Thus concludes our whirlwind tour of AFNetworking 2.0. New features for the next
You can start playing around with AFNetworking 2.0 by putting the following in your [`Podfile`](http://cocoapods.org):
-~~~{ruby}
+```ruby
platform :ios, '7.0'
pod "AFNetworking", "2.5.0"
-~~~
+```
For anyone coming over to AFNetworking from the current 1.x release, you may find [the AFNetworking 2.0 Migration Guide](https://github.com/AFNetworking/AFNetworking/wiki/AFNetworking-2.0-Migration-Guide) especially useful.
diff --git a/2013-09-23-ios7.md b/2013-09-23-ios7.md
index 3c7244a0..6743d0db 100644
--- a/2013-09-23-ios7.md
+++ b/2013-09-23-ios7.md
@@ -1,7 +1,7 @@
---
title: "iOS 7"
-author: Mattt Thompson
-category: ""
+author: Mattt
+category: Miscellaneous
excerpt: "With the NDA finally lifted, we can finally talk about all of the amazing new APIs in iOS 7."
status:
swift: 2.0
@@ -16,27 +16,27 @@ We'll be going over many of the new features iOS 7 in depth over the coming week
## NSData (NSDataBase64Encoding)
-[Base64](http://en.wikipedia.org/wiki/Base64) is a general term for encoding binary data as ASCII text. This is used all over the place on the web, since many core technologies are designed to support text, but not raw binary. For instance, CSS can embed images with [inline `data://` URIs](http://en.wikipedia.org/wiki/Data_URI_scheme), which are often Base64-encoded. Another example is [Basic `Authentication` headers](http://en.wikipedia.org/wiki/Basic_access_authentication), which Base64-encodes its username/password pair, which is marginally better than having them completely in the clear.
+[Base64](https://en.wikipedia.org/wiki/Base64) is a general term for encoding binary data as ASCII text. This is used all over the place on the web, since many core technologies are designed to support text, but not raw binary. For instance, CSS can embed images with [inline `data://` URIs](https://en.wikipedia.org/wiki/Data_URI_scheme), which are often Base64-encoded. Another example is [Basic `Authentication` headers](https://en.wikipedia.org/wiki/Basic_access_authentication), which Base64-encodes its username/password pair, which is marginally better than having them completely in the clear.
For the longest time, this boringly essential function was completely MIA, leaving thousands of developers to copy-paste random code snippets from forum threads. It was an omission as conspicuous and annoying as JSON pre-iOS 5.
But no longer! iOS 7 finally bakes-in Base64:
-~~~{swift}
+```swift
let string = "Lorem ipsum dolor sit amet."
if let data = string.dataUsingEncoding(NSUTF8StringEncoding) {
let base64EncodedString = data.base64EncodedStringWithOptions([])
print(base64EncodedString) // TG9yZW0gaXBzdW0gZG9sYXIgc2l0IGFtZXQu
}
-~~~
+```
-~~~{objective-c}
+```objc
NSString *string = @"Lorem ipsum dolor sit amet.";
NSString *base64EncodedString = [[string dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:0];
NSLog(@"%@", base64EncodedString); // TG9yZW0gaXBzdW0gZG9sYXIgc2l0IGFtZXQu
-~~~
+```
## NSURLComponents & NSCharacterSet (NSURLUtilities)
@@ -44,24 +44,24 @@ Foundation is blessed with a wealth of functionality for working with URIs. Unfo
`NSURLComponents` dramatically improves this situation. Think of it as `NSMutableURL`:
-~~~{swift}
-if let components = NSURLComponents(string: "http://nshipster.com") {
+```swift
+if let components = NSURLComponents(string: "https://nshipster.com") {
components.path = "/iOS7"
components.query = "foo=bar"
print(components.scheme!) // http
- print(components.URL!) // http://nshipster.com/iOS7?foo=bar
+ print(components.URL!) // https://nshipster.com/iOS7?foo=bar
}
-~~~
+```
-~~~{objective-c}
-NSURLComponents *components = [NSURLComponents componentsWithString:@"http://nshipster.com"];
+```objc
+NSURLComponents *components = [NSURLComponents componentsWithString:@"https://nshipster.com"];
components.path = @"/iOS7";
components.query = @"foo=bar";
NSLog(@"%@", components.scheme); // http
-NSLog(@"%@", [components URL]); // http://nshipster.com/iOS7?foo=bar
-~~~
+NSLog(@"%@", [components URL]); // https://nshipster.com/iOS7?foo=bar
+```
Each property for URL components also has a `percentEncoded*` variation (e.g. `user` & `percentEncodedUser`), which forgoes any additional URI percent encoding of special characters.
@@ -82,23 +82,23 @@ Anything with a notion of completed and total units is a candidate for `NSProgre
`NSProgress` can be used to simply report overall progress in a localized way:
-~~~{swift}
+```swift
let progress = NSProgress(totalUnitCount: 100)
progress.completedUnitCount = 42;
print(progress.localizedDescription) // 42% completed
-~~~
+```
-~~~{objective-c}
+```objc
NSProgress *progress = [NSProgress progressWithTotalUnitCount:100];
progress.completedUnitCount = 42;
NSLog(@"%@", [progress localizedDescription]); // 42% completed
-~~~
+```
...or it can be given a handler for stopping work entirely:
-~~~{swift}
+```swift
let timer = NSTimer(timeInterval: 1.0, target: self, selector: "incrementCompletedUnitCount:",
userInfo: nil, repeats: true)
@@ -107,9 +107,9 @@ progress.cancellationHandler = {
}
progress.cancel()
-~~~
+```
-~~~{objective-c}
+```objc
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0
target:self
selector:@selector(incrementCompletedUnitCount:) userInfo:nil
@@ -120,7 +120,7 @@ progress.cancellationHandler = ^{
};
[progress cancel];
-~~~
+```
`NSProgress` makes a lot more sense in the context of OS X Mavericks, but for now, it remains a useful class for encapsulating the shared patterns of work units.
@@ -130,19 +130,19 @@ Rejoice! The `NSRangeException`-dodging convenience of `-lastObject` has finally
Behold!
-~~~{swift}
+```swift
let array = [1, 2, 3] as NSArray
print("First Object: \(array.firstObject)") // First Object: Optional(1)
print("Last Object: \(array.lastObject)") // Last Object: Optional(3)
-~~~
+```
-~~~{objective-c}
+```objc
NSArray *array = @[@1, @2, @3];
NSLog(@"First Object: %@", [array firstObject]); // First Object: 1
NSLog(@"Last Object: %@", [array lastObject]); // Last Object: 3
-~~~
+```
Refreshing!
@@ -154,7 +154,7 @@ Since iOS 5, the Core Image framework has provided facial detection and recognit
In yet another free app idea, here's a snippet that might be used by a camera that only saves pictures of smiling faces:
-~~~{swift}
+```swift
import CoreImage
let smileDetector = CIDetector(ofType: CIDetectorTypeFace, context: context,
@@ -167,9 +167,9 @@ if let feature = features.first as? CIFaceFeature where feature.hasSmile {
} else {
label.text = "Say Cheese!"
}
-~~~
+```
-~~~{objective-c}
+```objc
@import CoreImage;
CIDetector *smileDetector = [CIDetector detectorOfType:CIDetectorTypeFace
@@ -182,13 +182,13 @@ if (([features count] > 0) && (((CIFaceFeature *)features[0]).hasSmile)) {
} else {
self.label.text = @"Say Cheese!"
}
-~~~
+```
## AVCaptureMetaDataOutput
Scan UPCs, QR codes, and barcodes of all varieties with `AVCaptureMetaDataOutput`, new to iOS 7. All you need to do is set it up as the output of an `AVCaptureSession`, and implement the `captureOutput:didOutputMetadataObjects:fromConnection:` method accordingly:
-~~~{swift}
+```swift
import AVFoundation
let session = AVCaptureSession()
@@ -226,9 +226,9 @@ func captureOutput(
print("QRCode: \(QRCode)")
}
-~~~
+```
-~~~{objective-c}
+```objc
@import AVFoundation;
AVCaptureSession *session = [[AVCaptureSession alloc] init];
@@ -267,7 +267,7 @@ didOutputMetadataObjects:(NSArray *)metadataObjects
NSLog(@"QR Code: %@", QRCode);
}
-~~~
+```
`AVFoundation` supports every code you've heard of (and probably a few that you haven't):
@@ -288,46 +288,46 @@ If nothing else, `AVCaptureMetaDataOutput` makes it possible to easily create a
Even though the number of people who have actually read something saved for later is only marginally greater than [the number of people who have ever used a QR code](http://picturesofpeoplescanningqrcodes.tumblr.com), it's nice that iOS 7 adds a way to add items to the Safari reading list with the new Safari Services framework.
-~~~{swift}
+```swift
import SafariServices
-let url = NSURL(string: "http://nshipster.com/ios7")!
+let url = NSURL(string: "https://nshipster.com/ios7")!
try? SSReadingList.defaultReadingList()?.addReadingListItemWithURL(url, title: "NSHipster", previewText: "...")
-~~~
+```
-~~~{objective-c}
+```objc
@import SafariServices;
-NSURL *URL = [NSURL URLWithString:@"http://nshipster.com/ios7"];
+NSURL *URL = [NSURL URLWithString:@"https://nshipster.com/ios7"];
[[SSReadingList defaultReadingList] addReadingListItemWithURL:URL
title:@"NSHipster"
previewText:@"..."
error:nil];
-~~~
+```
## AVSpeechSynthesizer
-Text-to-Speech has been the killer feature of computers for [accessibility](http://nshipster.com/uiaccessibility/) and [pranking](http://nshipster.com/icloud/) enthusiasts since its inception in the late 1960s.
+Text-to-Speech has been the killer feature of computers for [accessibility](https://nshipster.com/uiaccessibility/) and [pranking](https://nshipster.com/icloud/) enthusiasts since its inception in the late 1960s.
-iOS 7 brings the power of Siri with the convenience of a [Speak & Spell](http://en.wikipedia.org/wiki/Speak_%26_Spell_%28toy%29) in a new class `AVSpeechSynthesizer`:
+iOS 7 brings the power of Siri with the convenience of a [Speak & Spell](https://en.wikipedia.org/wiki/Speak_%26_Spell_%28toy%29) in a new class `AVSpeechSynthesizer`:
-~~~{swift}
+```swift
import AVFoundation
let synthesizer = AVSpeechSynthesizer()
let utterance = AVSpeechUtterance(string: "Just what do you think you're doing, Dave?")
utterance.rate = AVSpeechUtteranceMinimumSpeechRate // Tell it to me slowly
synthesizer.speakUtterance(utterance)
-~~~
+```
-~~~{objective-c}
+```objc
@import AVFoundation;
AVSpeechSynthesizer *synthesizer = [[AVSpeechSynthesizer alloc] init];
AVSpeechUtterance *utterance = [AVSpeechUtterance speechUtteranceWithString:@"Just what do you think you're doing, Dave?"];
utterance.rate = AVSpeechUtteranceMinimumSpeechRate; // Tell it to me slowly
[synthesizer speakUtterance:utterance];
-~~~
+```
## MKDistanceFormatter
@@ -335,7 +335,7 @@ Finally, we end our showcase of iOS 7's new and noteworthy APIs with another cla
As advertised, `MKDistanceFormatter` provides a way to convert distances into localized strings using either imperial or metric units:
-~~~{swift}
+```swift
import CoreLocation
import MapKit
@@ -346,9 +346,9 @@ let distance = portland.distanceFromLocation(sanFrancisco)
let formatter = MKDistanceFormatter()
formatter.units = .Imperial
print(formatter.stringFromDistance(distance)) // 535 miles
-~~~
+```
-~~~{objective-c}
+```objc
@import CoreLocation;
@import MapKit;
@@ -359,7 +359,7 @@ CLLocationDistance distance = [portland distanceFromLocation:sanFrancisco];
MKDistanceFormatter *formatter = [[MKDistanceFormatter alloc] init];
formatter.units = MKDistanceFormatterUnitsImperial;
NSLog(@"%@", [formatter stringFromDistance:distance]); // 535 miles
-~~~
+```
---
diff --git a/2013-09-30-xcode-key-bindings-and-gestures.md b/2013-09-30-xcode-key-bindings-and-gestures.md
index 49ea63fc..b2df63e4 100644
--- a/2013-09-30-xcode-key-bindings-and-gestures.md
+++ b/2013-09-30-xcode-key-bindings-and-gestures.md
@@ -1,6 +1,6 @@
---
title: Xcode Key Bindings & Gestures
-author: Mattt Thompson
+author: Mattt
category: Xcode
tag: popular
excerpt: "Xcode key bindings and gestures not only shave off seconds of precious work, but make you look more confident, competent, and cromulent in the process."
@@ -12,13 +12,13 @@ The extent to which programming-as-craft is compared to manual disciplines like
Here at NSHipster, the advice is simple and only slightly allegorical: "Xcode is your mustache, so keep it trimmed, waxed to a sharp point, and free of bugs."
-Anyway, a few weeks ago, we looked at how [Xcode Snippets](http://nshipster.com/xcode-snippets/) can make you more productive by reducing the amount of boilerplate code you have to type out. This week, we're going to pick up on that thread and cover the essential key bindings and gestures.
+Anyway, a few weeks ago, we looked at how [Xcode Snippets](https://nshipster.com/xcode-snippets/) can make you more productive by reducing the amount of boilerplate code you have to type out. This week, we're going to pick up on that thread and cover the essential key bindings and gestures.
Xcode key bindings and gestures not only shave off seconds of precious work, but make you look more confident, competent, and cromulent in the process. Learn the following tricks of the trade and join the elite set of Xcode power users.
---
-> For your reference, here is a legend of the common modifier key symbols (as well as a symbol for click [shamelessly borrowed from the International Phonetic Alphabet](http://en.wikipedia.org/wiki/Click_consonant)):
+> For your reference, here is a legend of the common modifier key symbols (as well as a symbol for click [shamelessly borrowed from the International Phonetic Alphabet](https://en.wikipedia.org/wiki/Click_consonant)):
@@ -32,18 +32,18 @@ Xcode key bindings and gestures not only shave off seconds of precious work, but
-
⌘
-
⌃
-
⌥
-
⇧
-
ʘ
+
⌘
+
⌃
+
⌥
+
⇧
+
ʘ
## Open Quickly (`⇧⌘O`)
-
+
Learn to rely less on the Project Navigator by learning to love Open Quickly. There's a lot to love, too—with support for partial case- and position-insensitive matches, Xcode does a great job of finding what you want with just a minimal amount of input on your part.
@@ -51,7 +51,7 @@ Learn to rely less on the Project Navigator by learning to love Open Quickly. Th
## Quick Documentation (`⌥ʘ` on Symbol / Three-Finger Tap) Open Documentation (`⌥ʘʘ` on Symbol)
-
+
Quick Documentation is probably the first Xcode shortcut developers should learn. Just alt-click (or three-finger tap) any class, variable, or constant value, and Xcode will give you a quick rundown of what you're looking at. Alt-double-click to bring up the documentation window, opened to the relevant entry.
@@ -61,7 +61,7 @@ Also well-know to an expert Xcoder's workflow is Jump to Definition, which opens
## Jump to Next Counterpart (`^⌘↑` / `^⌘↓` / Three-Finger Vertical Swipe)
-
+
Last, but certainly not least, there's Jump to Next Counterpart, which is very likely the shortcut used the most on any given day. Quickly switch between a `.h` header and it's corresponding `.m` implementation with a simple three-finger swipe up or down (or `^⌘↑` / `^⌘↓` if you feel so inclined).
@@ -69,23 +69,23 @@ Last, but certainly not least, there's Jump to Next Counterpart, which is very l
## Comment Selection / Current Line (`⌘/`)
-
+
Sure, you _could_ be debugging the "right way" by setting breakpoints and being clever with your code paths, but there's quite so refreshingly simple and powerful as phasing code in and out of computational existence with a comment. Add or remove `//` comments to the current line or selection.
## Show Standard Editor (`⌘↵`) Show Assistant Editor (`⌥⌘↵`) Show Version Editor (`⌥⇧⌘↵`)
-
+
For how useful the Assistant Editor can be, surprisingly few developers can actually remember the key combo to turn it on and off. But now with `⌘↵` and `⌥⌘↵` fresh in your mind, you'll be helping Xcode help yourself more often.
-
+
As an aside, if you're not big on how editors are stacking, a different horizontal or vertical arrangement can be chosen in View > Assistant Editor.
---
-
+
Sandwiching the editors on the left and right flanks, the Navigator and Utilities panels encircle your code in their loving embrace. Learning how to get them to show what's useful and GTFO when needed are critical for inner peace and maximum productivity.
@@ -122,7 +122,7 @@ Sandwiching the editors on the left and right flanks, the Navigator and Utilitie
## Show / Hide Debug Area (`⇧⌘Y`) Activate Console (`⇧⌘C`)
-
+
Anyone miss the option in Xcode 3 to have a detached debugger window? Yeah, me too.
@@ -132,19 +132,19 @@ Knowing how to toggle the debug area and activate the console in a single keystr
## Find (`⌘F`) / Find & Replace (`⌥⌘F`) / Find in Project (`⇧⌘F`) / Find & Replace in Project (`⌥⇧⌘F`)
-
+
For when Xcode's refactoring capabilities come up short... which is to say: often. On the plus side, Xcode allows reference, definition, and regular expression search in addition to literal text.
## Spelling & Grammar (`⌘:`)
-
+
All-powerful as Clang is, it still can't help your nightmarish grammar and punctuation in your comments. Especially for anyone releasing code into the open-source wilds, do yourself a favor and give it a once-over with a built-in OS X spelling and grammar check.
---
-
+
But, of course, the fun doesn't stop there! Like any respectable editor, Xcode allows you to customize the key bindings for every menu item and action across the app.
diff --git a/2013-10-07-key-value-observing.md b/2013-10-07-key-value-observing.md
index 18c7ffbc..8ffb2016 100644
--- a/2013-10-07-key-value-observing.md
+++ b/2013-10-07-key-value-observing.md
@@ -1,6 +1,6 @@
---
title: Key-Value Observing
-author: Mattt Thompson
+author: Mattt
category: Cocoa
tag: nshipster, popular
excerpt: "Ask anyone who's been around the NSBlock a few times: Key-Value Observing has the _worst_ API in all of Cocoa. It's awkward, verbose, and confusing. And worst of all, its terrible API belies one of the most compelling features of the framework."
@@ -32,16 +32,16 @@ The main value proposition of KVO is rather compelling: any object can subscribe
## Subscribing
-Objects can have observers added for a particular key path, which, as described in [the KVC operators article](http://nshipster.com/kvc-collection-operators/), are dot-separated keys that specify a sequence of properties. Most of the time with KVO, these are just the top-level properties on the object.
+Objects can have observers added for a particular key path, which, as described in [the KVC operators article](https://nshipster.com/kvc-collection-operators/), are dot-separated keys that specify a sequence of properties. Most of the time with KVO, these are just the top-level properties on the object.
The method used to add an observer is `–addObserver:forKeyPath:options:context:`:
-~~~{objective-c}
+```objc
- (void)addObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
context:(void *)context
-~~~
+```
> - `observer`: The object to register for KVO notifications. The observer must implement the key-value observing method observeValueForKeyPath:ofObject:change:context:.
> - `keyPath`: The key path, relative to the receiver, of the property to observe. This value must not be `nil`.
@@ -73,28 +73,28 @@ Another aspect of KVO that lends to its ugliness is the fact that there is no wa
Instead, all changes for observers are funneled through a single method—`-observeValueForKeyPath:ofObject:change:context:`:
-~~~{objective-c}
+```objc
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
-~~~
+```
Those parameters are the same as what were specified in `–addObserver:forKeyPath:options:context:`, with the exception of `change`, which are populated from whichever `NSKeyValueObservingOptions` `options` were used.
A typical implementation of this method looks something like this:
-~~~{objective-c}
+```objc
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if ([keyPath isEqualToString:@"state"]) {
- // ...
+ <#...#>
}
}
-~~~
+```
Depending on how many kinds of objects are being observed by a single class, this method may also introduce `-isKindOfObject:` or `-respondsToSelector:` in order to definitively identify the kind of event being passed. However, the safest method is to do an equality check to `context`—especially when dealing with subclasses whose parents observe the same keypath.
@@ -102,13 +102,13 @@ Depending on how many kinds of objects are being observed by a single class, thi
What makes a good `context` value? Here's a suggestion:
-~~~{objective-c}
+```objc
static void * XXContext = &XXContext;
-~~~
+```
It's that simple: a static value that stores its own pointer. It means nothing on its own, which makes it rather perfect for ``:
-~~~{objective-c}
+```objc
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
@@ -120,7 +120,7 @@ It's that simple: a static value that stores its own pointer. It means nothing o
}
}
}
-~~~
+```
### Better Key Paths
@@ -128,13 +128,13 @@ Passing strings as key paths is strictly worse than using properties directly, a
A clever workaround to this is to use `NSStringFromSelector` and a `@selector` literal value:
-~~~{objective-c}
+```objc
NSStringFromSelector(@selector(isFinished))
-~~~
+```
Since `@selector` looks through all available selectors in the target, this won't prevent all mistakes, but it will catch most of them—including breaking changes made by Xcode automatic refactoring.
-~~~{objective-c}
+```objc
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
@@ -145,10 +145,10 @@ Since `@selector` looks through all available selectors in the target, this won'
}
} else if (...) {
- // ...
+ <#...#>
}
}
-~~~
+```
## Unsubscribing
@@ -160,7 +160,7 @@ Perhaps the most pronounced annoyance with KVO is how it gets you at the end. If
Which causes one to rely on a rather unfortunate cudgel `@try` with an unhandled `@catch`:
-~~~{objective-c}
+```objc
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
@@ -175,7 +175,7 @@ Which causes one to rely on a rather unfortunate cudgel `@try` with an unhandled
}
}
}
-~~~
+```
Granted, _not_ handling a caught exception, as in this example, is waving the `[UIColor whiteColor]` flag of surrender. Therefore, one should only really use this technique when faced with intermittent crashes which cannot be remedied by normal book-keeping (whether due to race conditions or undocumented behavior from a superclass).
@@ -189,11 +189,11 @@ But what about compound or derived values? Let's say you have an object with a `
Well, you can implement the method `keyPathsForValuesAffectingAddress` (or its less magical catch-all, `+keyPathsForValuesAffectingValueForKey:`):
-~~~{objective-c}
+```objc
+ (NSSet *)keyPathsForValuesAffectingAddress {
return [NSSet setWithObjects:NSStringFromSelector(@selector(streetAddress)), NSStringFromSelector(@selector(locality)), NSStringFromSelector(@selector(region)), NSStringFromSelector(@selector(postalCode)), nil];
}
-~~~
+```
---
diff --git a/2013-10-14-nserror.md b/2013-10-14-nserror.md
index 4125051f..3f0fbd9c 100644
--- a/2013-10-14-nserror.md
+++ b/2013-10-14-nserror.md
@@ -1,6 +1,6 @@
---
title: NSError
-author: Mattt Thompson
+author: Mattt
category: Cocoa
tags: nshipster
excerpt: "NSError is the unsung hero of the Foundation framework. Passed gallantly in and out of perilous method calls, it is the messenger by which we are able to contextualize our failures."
@@ -51,7 +51,7 @@ Three are generally useful:
Here's how to construct `NSError` with a `userInfo` dictionary:
-~~~{objective-c}
+```objc
NSDictionary *userInfo = @{
NSLocalizedDescriptionKey: NSLocalizedString(@"Operation was unsuccessful.", nil),
NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"The operation timed out.", nil),
@@ -60,19 +60,19 @@ NSDictionary *userInfo = @{
NSError *error = [NSError errorWithDomain:NSHipsterErrorDomain
code:-57
userInfo:userInfo];
-~~~
+```
The advantage of encapsulating this information in an object like `NSError`, as opposed to, say, throwing exceptions willy-nilly, is that these error objects can be easily passed between different objects and contexts.
For example, a controller that calls a method that populates an `NSError **` (as discussed in the next section) might pass that error into an alert view:
-~~~{objective-c}
+```objc
[[[UIAlertView alloc] initWithTitle:error.localizedDescription
message:error.localizedRecoverySuggestion
delegate:nil
cancelButtonTitle:NSLocalizedString(@"OK", nil)
otherButtonTitles:nil, nil] show];
-~~~
+```
> As a brief non-sequitur: one clever hack used by C functions to communicate errors is to [encode 4-letter ASCII sequences in the 32 bit return type](https://github.com/mattt/Xcode-Snippets/blob/master/checkerror.m). It's no `localizedDescription`, but it's better than cross-referencing error codes from a table every time!
@@ -96,7 +96,7 @@ There are two ways in which you will encounter `NSError`: as a consumer and as a
As a consumer, you are primarily concerned with methods that have a final parameter of type `NSError **`. Again, this is to get around the single return value constraint of Objective-C; by passing a pointer to an uninitialized `NSError *` variable, that variable will be populated with any error the method encounters:
-~~~{objective-c}
+```objc
NSError *error = nil;
BOOL success = [[NSFileManager defaultManager] moveItemAtPath:@"/path/to/target"
toPath:@"/path/to/destination"
@@ -104,13 +104,13 @@ BOOL success = [[NSFileManager defaultManager] moveItemAtPath:@"/path/to/target"
if (!success) {
NSLog(@"%@", error);
}
-~~~
+```
> According to Cocoa conventions, methods returning `BOOL` to indicate success or failure are encouraged to have a final `NSError **` parameter if there are multiple failure conditions to distinguish between. A good guideline is whether you could imagine that `NSError` bubbling up, and being presented to the user.
Another way `NSError` objects are passed is the inclusion of an `NSError *` argument in `completionHandler` blocks. This gets around both a constraint on single value returns as well as one on that value being returned synchronously. This has become especially popular with newer Foundation APIs, like `NSURLSession`:
-~~~{objective-c}
+```objc
NSURL *URL = [NSURL URLWithString:@"http://example.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
@@ -119,10 +119,10 @@ NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConf
if (!data) {
NSLog(@"%@", error);
} else {
- // ...
+ <#...#>
}
}] resume];
-~~~
+```
### Producing
@@ -130,7 +130,7 @@ One would be well-advised to follow the same conventions for error handling as o
To pass an error to an `NSError **` parameter, do the following:
-~~~{objective-c}
+```objc
- (BOOL)validateObject:(id)object
error:(NSError * __autoreleasing *)outError
{
@@ -148,7 +148,7 @@ To pass an error to an `NSError **` parameter, do the following:
return success;
}
-~~~
+```
The root error, if one exists, should be returned as part of your custom error's `userInfo` dictionary as the value for `NSUnderlyingErrorKey`.
diff --git a/2013-10-21-launch-arguments-and-environment-variables.md b/2013-10-21-launch-arguments-and-environment-variables.md
index f3d39718..0c646a0b 100644
--- a/2013-10-21-launch-arguments-and-environment-variables.md
+++ b/2013-10-21-launch-arguments-and-environment-variables.md
@@ -1,10 +1,10 @@
---
-title: "Launch Arguments & Environment Variables"
-author: Mattt Thompson
+title: "Launch Arguments & Environment Variables"
+author: Mattt
category: Xcode
excerpt: "There are a number of options that can be passed into a target's scheme to enable useful debugging behavior, but like a fast food secret menu, they're obscure and widely unknown."
status:
- swift: n/a
+ swift: n/a
---
Walk into any American fast food establishment, and you'll be greeted with a colorful, back-lit display of specials, set menus, and other a la carte items. But as those in-the-know are quick to point out, larger chains often have a _secret_ menu, passed down by oral tradition between line cook workers and patrons over the generations.
@@ -19,15 +19,15 @@ Which brings us to Xcode Launch Arguments & Environment Variables. There are a n
So this week on NSHipster, we'll take a look at the hidden world of Xcode runtime configuration, so that you, dear reader, may also saunter up to the great lunch counter of Objective-C and order to your heart's content.
-* * *
+---
To enable launch arguments and set environment variables for your app, select your target from the Xcode toolbar and select "Edit Scheme..."
-
+
On the left side of the panel, select "Run [AppName].app", and select the "Arguments" segment on the right side. There will be two drop-downs, for "Arguments Passed on Launch" and "Environment Variables".
-
+
For the purposes of debugging an app target, launch arguments and environment variables can be thought to be equivalent—both change the runtime behavior by defining certain values. In practice, the main difference between the two is that launch arguments begin with a dash (`-`) and don't have a separate field for argument values.
@@ -39,13 +39,15 @@ Any argument passed on launch will override the current value in `NSUserDefaults
Getting localization right is a challenging and time-consuming task in and of itself. Fortunately, there are a few launch arguments that make the process _much_ nicer.
-> For more information about localization, check out our article about [`NSLocalizedString`](http://nshipster.com/nslocalizedstring/).
+> For more information about localization, check out our article about [`NSLocalizedString`](https://nshipster.com/nslocalizedstring/).
#### NSDoubleLocalizedStrings
To simulate German's UI-breaking _götterdämmere Weltanschauung_ of long-compound-words-unbroken-by-breakable-whitespace, there's `NSDoubleLocalizedStrings`.
-According to [IBM's Globalization Guidelines](http://www-01.ibm.com/software/globalization/guidelines/a3.html), we can expect translations from English to many European languages to be double or even triple the physical space of the source:
+According to [IBM's Globalization Guidelines _(Archived)_](https://web.archive.org/web/20180704133337/http://www-01.ibm.com/software/globalization/guidelines/a3.html), we can expect translations from English to many European languages to be double or even triple the physical space of the source:
+
+{::nomarkdown}
@@ -53,6 +55,7 @@ According to [IBM's Globalization Guidelines](http://www-01.ibm.com/software/glo
Number of Characters in Text
Additional Physical Space Required
+
≤ 10
100% to 200%
11 – 20
80% to 100%
@@ -62,14 +65,15 @@ According to [IBM's Globalization Guidelines](http://www-01.ibm.com/software/glo
70
30%
+{:/}
While you're waiting for the first batch of translations to come back, or are merely curious to see how badly your UI breaks under linguistic pressure, specify the following launch argument:
-~~~
+```
-NSDoubleLocalizedStrings YES
-~~~
+```
-
+
#### NSShowNonLocalizedStrings
@@ -77,9 +81,9 @@ Project managers screaming at you to get localization finished? Now you can conf
If you pass the `NSShowNonLocalizedStrings` launch argument, any unlocalized string will SCREAM AT YOU IN CAPITAL LETTERS. HOW DELIGHTFUL!
-~~~
+```
-NSShowNonLocalizedStrings YES
-~~~
+```
#### AppleLanguages
@@ -87,9 +91,9 @@ Perhaps the most useful launch argument of all, however, is `AppleLanguages`.
Normally, one would have to manually go through Settings > General > International > Language and wait for the Simulator or Device to restart. But the same can be accomplished much more simply with the following launch argument:
-~~~
+```
-AppleLanguages (es)
-~~~
+```
> The value for `AppleLanguages` can either be the name of the language ("Spanish"), or its language code (`es`), but since localization files are keyed by their ISO 639 code, using the code is preferable to the actual name of the language.
@@ -103,19 +107,19 @@ Most Core Data stacks use SQLite as a persistent store, so if your app is anythi
Set the following launch argument:
-~~~
+```
-com.apple.CoreData.SQLDebug 3
-~~~
+```
...and let the spice flow.
-~~~
+```
CoreData: sql: pragma cache_size=1000
CoreData: sql: SELECT Z_VERSION, Z_UUID, Z_PLIST FROM Z_METADATA
CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZAUTHOR, t0.ZTITLE, t0.ZCOPYRIGHT FROM ZBOOK t0 ORDER BY t0.ZAUTHOR, t0.ZTITLE
CoreData: annotation: sql connection fetch time: 0.0001s
CoreData: annotation: total fetch execution time: 0.0010s for 20 rows.
-~~~
+```
`com.apple.CoreData.SQLDebug` takes a value between `1` and `3`; the higher the value, the more verbose the output. Adjust according to taste.
@@ -123,19 +127,19 @@ CoreData: annotation: total fetch execution time: 0.0010s for 20 rows.
Want your debug statements to be _even spicier_? Toss `com.apple.CoreData.SyntaxColoredLogging` into the mix and brace yourself for an explosion of color:
-~~~
+```
-com.apple.CoreData.SyntaxColoredLogging YES
-~~~
+```
#### Migration Debug
In any other persistence layer, migrations are a blessing. Yet, for some reason, Core Data manages to make them into something out of a nightmare. When things go wrong and you have no one to blame except your own ignorant self, unworthy of such an intuitive and well-designed ORM graph persistence framework, then here's an argument you'll want to pass at launch:
-~~~
+```
-com.apple.CoreData.MigrationDebug
-~~~
+```
-* * *
+---
## Environment Variables
@@ -151,16 +155,19 @@ Over-played in popular media, under-played in Objective-C, everyone can agree th
Setting `NSZombie`-related environment variables allows you to control the _BRAAAAINS!_ of your app. To be more specific, when objects are deallocated, they become "zombified", able to communicate any messages that are passed after they have been freed. This can be useful for tracing any errant `EXC_BAD_ACCESS` exceptions you get during execution.
+{::nomarkdown}
+
Name
Effect
-
NSZombieEnabled
If set to YES, deallocated objects are 'zombified'; this allows you to quickly debug problems where you send a message to an object that has already been freed.
-
NSDeallocateZombies
If set to YES, the memory for 'zombified' objects is actually freed.
+
NSZombieEnabled
If set to YES, deallocated objects are 'zombified'; this allows you to quickly debug problems where you send a message to an object that has already been freed.
+
NSDeallocateZombies
If set to YES, the memory for 'zombified' objects is actually freed.
+{:/}
### Memory Allocator
@@ -170,37 +177,42 @@ The memory allocator includes several debugging hooks that can be enabled by env
Here are some of the most useful ones:
+{::nomarkdown}
+
Name
Effect
-
MallocScribble
Fill allocated memory with 0xAA and scribble deallocated memory with 0x55.
-
MallocGuardEdges
Add guard pages before and after large allocations.
-
MallocStackLogging
Record backtraces for each memory block to assist memory debugging tools; if the block is allocated and then immediately freed, both entries are removed from the log, which helps reduce the size of the log.
-
MallocStackLoggingNoCompact
Same as MallocStackLogging but keeps all log entries.
+
MallocScribble
Fill allocated memory with 0xAA and scribble deallocated memory with 0x55.
+
MallocGuardEdges
Add guard pages before and after large allocations.
+
MallocStackLogging
Record backtraces for each memory block to assist memory debugging tools; if the block is allocated and then immediately freed, both entries are removed from the log, which helps reduce the size of the log.
+
MallocStackLoggingNoCompact
Same as MallocStackLogging but keeps all log entries.
+{:/}
### I/O Buffering
Although unlikely, you may come across a situation where you want logging to `stdout` to be unbuffered (ensuring that the output has been written before continuing). You can set that with the `NSUnbufferedIO` environment variable:
+{::nomarkdown}
+
Name
Effect
-
NSUnbufferedIO
If set to YES, Foundation will use unbuffered I/O for stdout (stderr is unbuffered by default).
+
NSUnbufferedIO
If set to YES, Foundation will use unbuffered I/O for stdout (stderr is unbuffered by default).
+{:/}
-* * *
+---
-Just as secret menus are bound by the implications of Gödel's Incompleteness Theorem, it is impossible to document all of the secret incantations to get special treatment in Xcode. However, perhaps you can find a few more (and learn a _ton_ about runtime internals) by perusing Apple's [Technical Note TN2239: iOS Debugging Magic][TN2239] and [Technical Note TN2124: OS X Debugging Magic][TN2124].
+Just as secret menus are bound by the implications of Gödel's Incompleteness Theorem, it is impossible to document all of the secret incantations to get special treatment in Xcode. However, perhaps you can find a few more (and learn a _ton_ about runtime internals) by perusing Apple's [Technical Note TN2239: iOS Debugging Magic][tn2239] and [Technical Note TN2124: OS X Debugging Magic][tn2124].
Hopefully, though, the secret knowledge you've been exposed to in this article will sustain you in your app endeavors. Use them wisely, and pass them onto your coworkers like an urban legend or juicy rumor.
-
-[TN2239]: https://developer.apple.com/library/ios/technotes/tn2239/_index.html
-[TN2124]: https://developer.apple.com/library/mac/technotes/tn2124/_index.html
+[tn2239]: https://developer.apple.com/library/ios/technotes/tn2239/_index.html
+[tn2124]: https://developer.apple.com/library/mac/technotes/tn2124/_index.html
diff --git a/2013-10-28-nshipster-quiz-4.md b/2013-10-28-nshipster-quiz-4.md
index 9d9a8769..8a5caf70 100644
--- a/2013-10-28-nshipster-quiz-4.md
+++ b/2013-10-28-nshipster-quiz-4.md
@@ -1,6 +1,6 @@
---
title: "NSHipster Quiz #4"
-author: Mattt Thompson
+author: Mattt
category: Trivia
excerpt: "The fourth and final quiz of the year. Do you have what it takes to be the `NSArray -firstObject` among your peers?"
status:
@@ -9,7 +9,7 @@ status:
The fourth and final NSHipster pub quiz of the year was held in the beautiful city of Amsterdam on October 22nd, with help from the good folks at [Appsterdam](http://appsterdam.rs), [The Big Nerd Ranch](http://www.bignerdranch.com/), and [Heroku](http://www.heroku.com).
-The competition was fierce, but ultimately the team of [Mike Lee](https://twitter.com/bmf), [Judy Chen](https://twitter.com/judykitteh), [Eloy Dúran](https://twitter.com/alloy), [Alexander Repty](https://twitter.com/arepty), [Maxie Ketschau-Repty](https://twitter.com/Yumyoko), and [Sernin van de Krol](https://twitter.com/paneidos) were victorious. This was, by design, to be the highest-scoring of any pub quiz, with generous portions of extra points, and Team "[Graceful Hoppers](http://en.wikipedia.org/wiki/Grace_Hopper)" came through with an impressive 53 points (which, interestingly enough, only edged out the 2nd place team by ½ of a point).
+The competition was fierce, but ultimately the team of [Mike Lee](https://twitter.com/bmf), [Judy Chen](https://twitter.com/judykitteh), [Eloy Dúran](https://twitter.com/alloy), [Alexander Repty](https://twitter.com/arepty), [Maxie Ketschau-Repty](https://twitter.com/Yumyoko), and [Sernin van de Krol](https://twitter.com/paneidos) were victorious. This was, by design, to be the highest-scoring of any pub quiz, with generous portions of extra points, and Team "[Graceful Hoppers](https://en.wikipedia.org/wiki/Grace_Hopper)" came through with an impressive 53 points (which, interestingly enough, only edged out the 2nd place team by ½ of a point).
As always, you can play along at home or at work with your colleagues. Here are the rules:
@@ -53,25 +53,25 @@ With the fluff out of the way, it's now time to dive into some hardcore Cocoa fu
9. Name the 4 Classes that conform to `` (1 pt. each)
10. What is the name of the method called by the following code:
-~~~{objective-c}
+```objc
array[1] = @"foo";
-~~~
+```
Round 3: Picture Round - Indie Devs
-----------------------------------
Following another tradition of the NSHipster quiz is everybody's favorite: the Picture Round! This time, the theme is indie developers. Earn up to 3 points for each set of pictures by naming the **founder**, the **name of the company** they're known for, and the **name of their flagship app** represented by the icon.
-1. 
-2. 
-3. 
-4. 
-5. 
-6. 
-7. 
-8. 
-9. 
-10. 
+1. 
+2. 
+3. 
+4. 
+5. 
+6. 
+7. 
+8. 
+9. 
+10. 
Round 4: NSAnagram
------------------
diff --git a/2013-11-04-xctool.md b/2013-11-04-xctool.md
index d71482b1..3749c0b2 100644
--- a/2013-11-04-xctool.md
+++ b/2013-11-04-xctool.md
@@ -1,10 +1,11 @@
---
title: xctool
-author: Mattt Thompson
+author: Mattt
category: Open Source
excerpt: "Control the build system, and you control the destiny of the language, its ecosystem, and community."
+retired: true
status:
- swift: n/a
+ swift: n/a
---
> Control the build system, and you control the destiny of the language, its ecosystem, and community.
@@ -13,11 +14,13 @@ Objective-C has changed so very much in such a very short timespan. In just a fe
[CocoaPods](http://cocoapods.org), in particular, exemplifies the compounding influences of technology and community. Now two years into the project, there are over 2,700 community-submitted libraries and frameworks that can be integrated into your own project with a simple `pod install` command.
-Dependency management is just one of the many aspects of iOS and OS X development that have been dramatically improved by the community. Other examples include [provisioning and distribution automation](http://nomad-cli.com), [bug reporting](http://www.quickradar.com), and [documentation](http://cocoadocs.org).
+Dependency management is just one of the many aspects of iOS and OS X development that have been dramatically improved by the community. Other examples include [provisioning and distribution automation](http://nomad-cli.com),
+[bug reporting](https://nshipster.com/bug-reporting),
+and [documentation](http://cocoadocs.org).
But this week, our focus is on a tool that redefines the actual build process itself, to serve as the foundation for a new generation of tooling and integration: `xctool`.
-* * *
+---
[`xctool`](https://github.com/facebook/xctool) is an open source project by [Fred Potter](https://github.com/fpotter) that came from his work on build system automation at [Facebook](https://github.com/facebook). It is a drop-in replacement for [`xcodebuild`](https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/xcodebuild.1.html), the utility underlying Xcode.app itself.
@@ -31,13 +34,15 @@ Rather than belabor the point by enumerating all of the flaws in this decade-old
The first you'll notice about `xctool` is its gorgeous, colorized output.
-
+
As consumers of Apple hardware and software ourselves, the role of design cannot be under-stated. In this respect, `xctool` absolutely nails it. Every step of the build process is neatly organized and reported in a way that is understandable and visually appealing, with ANSI colorization and a splash of Unicode ornamentation.
But xctool's beauty is not just skin-deep: build progress can also be reported in formats that can be read by other tools:
- xctool -reporter plain:output.txt build
+```terminal
+$ xctool -reporter plain:output.txt build
+```
#### Reporters
@@ -62,13 +67,13 @@ To add Travis CI to your own Objective-C project, [create an account and webserv
#### .travis.yml
-~~~{yaml}
+```yaml
language: objective-c
before_install:
- - brew update
- - brew install xctool
+ - brew update
+ - brew install xctool
script: xctool -workspace MyApp.xcworkspace -scheme MyApp test
-~~~
+```
### OCLint
@@ -78,9 +83,9 @@ Remember `xctool`'s `json-compilation-database` reporter option? Well, that outp
At the time of writing, there is still a ways to go before this becomes widely adopted, but my hope is that, now that the cat is out of the bag, some enterprising individuals might work together to make an insanely great experience around this promising tool.
-* * *
+---
-Just as with the growth population within cities, infrastructure makes all of difference. One way or another, whether by local [government mandate](http://en.wikipedia.org/wiki/Commissioners'_Plan_of_1811), [emergent self-organization](http://en.wikipedia.org/wiki/Kowloon_Walled_City), or somewhere in-between, environments are changed to accommodate this growth.
+Just as with the growth population within cities, infrastructure makes all of difference. One way or another, whether by local [government mandate](https://en.wikipedia.org/wiki/Commissioners'_Plan_of_1811), [emergent self-organization](https://en.wikipedia.org/wiki/Kowloon_Walled_City), or somewhere in-between, environments are changed to accommodate this growth.
Objective-C has and continues to undergo rapid growth based on the popularity of iOS. It is up to the community, in working with (and sometimes against) Apple to create infrastructure necessary to integrate so many new developers. How successful we are in this respect determines how we understand and communicate our roles and responsibilities as professional developers.
diff --git a/2013-11-11-nsformatter.md b/2013-11-11-nsformatter.md
deleted file mode 100644
index bce157e7..00000000
--- a/2013-11-11-nsformatter.md
+++ /dev/null
@@ -1,670 +0,0 @@
----
-title: NSFormatter
-author: Mattt Thompson
-category: Cocoa
-tags: nshipster, popular
-excerpt: "Conversion is the tireless errand of software development. Most programming tasks boil down to some variation of transforming data into something more useful."
-revisions:
- "2014-06-30": Converted examples to Swift; added iOS 8 & OS X Yosemite formatter classes.
- "2015-02-17": Converted remaining examples to Swift; reintroduced Objective-C examples; added Objective-C examples for new formatter classes.
-status:
- swift: 2.0
- reviewed: September 8, 2015
----
-
-Conversion is the tireless errand of software development. Most programming tasks boil down to some variation of transforming data into something more useful.
-
-In the case of user-facing software, converting data into human-readable form is an essential task, and a complex one at that. A user's preferred language, locale, calendar, or currency can all factor into how information should be displayed, as can other constraints, such as a label's dimensions.
-
-All of this is to say that sending `-description` to an object just isn't going to cut it in most circumstances. Even `+stringWithFormat:` is going to ultimately disappoint. No, the real tool for this job is `NSFormatter`.
-
-* * *
-
-`NSFormatter` is an abstract class for transforming data into a textual representation. It can also interpret valid textual representations back into data.
-
-Its origins trace back to `NSCell`, which is used to display information and accept user input in tables, form fields, and other views in AppKit. Much of the API design of `NSFormatter` reflects this.
-
-Foundation provides a number of concrete subclasses for `NSFormatter` (in addition to a single `NSFormatter` subclass provided in the MapKit framework):
-
-| Class | Availability |
-|------------------------------|--------------------------------|
-| `NSNumberFormatter` | iOS 2.0 / OS X Cheetah |
-| `NSDateFormatter` | iOS 2.0 / OS X Cheetah |
-| `NSByteCountFormatter` | iOS 6.0 / OS X Mountain Lion |
-| `NSDateComponentsFormatter` | iOS 8.0 / OS X Yosemite |
-| `NSDateIntervalFormatter` | iOS 8.0 / OS X Yosemite |
-| `NSEnergyFormatter` | iOS 8.0 / OS X Yosemite |
-| `NSMassFormatter` | iOS 8.0 / OS X Yosemite |
-| `NSLengthFormatter` | iOS 8.0 / OS X Yosemite |
-| `MKDistanceFormatter` | iOS 7.0 / OS X Mavericks |
-
-As some of the oldest members of the Foundation framework, `NSNumberFormatter` and `NSDateFormatter` are astonishingly well-suited to their respective domains, in that way only decade-old software can. This tradition of excellence is carried by the most recent incarnations as well.
-
-> iOS 8 & OS X Yosemite more than _doubled_ the number of system-provided formatter classes, which is pretty remarkable.
-
-## NSNumberFormatter
-
-`NSNumberFormatter` handles every aspect of number formatting imaginable, from mathematical and scientific notation, to currencies and percentages. Nearly everything about the formatter can be customized, whether it's the currency symbol, grouping separator, number of significant digits, rounding behavior, fractions, character for infinity, string representation for `0`, or maximum / minimum values. It can even write out numbers in several languages!
-
-### Number Styles
-
-When using an `NSNumberFormatter`, the first order of business is to determine what kind of information you're displaying. Is it a price? Is this a whole number, or should decimal values be shown?
-
-`NSNumberFormatter` can be configured for any one of the following formats, with the `numberStyle` property.
-
-To illustrate the differences between each style, here is how the number `12345.6789` would be displayed for each:
-
-#### `NSNumberFormatterStyle`
-
-| Formatter Style | Output |
-|--------------------|---------------------------------------------------------------------|
-| `NoStyle` | 12346 |
-| `DecimalStyle` | 12345.6789 |
-| `CurrencyStyle` | $12345.68 |
-| `PercentStyle` | 1234567% |
-| `ScientificStyle` | 1.23456789E4 |
-| `SpellOutStyle` | twelve thousand three hundred forty-five point six seven eight nine |
-
-### Locale Awareness
-
-By default, `NSNumberFormatter` will format according to the current locale settings, which determines things like currency symbol ($, £, €, etc.) and whether to use "," or "." as the decimal separator.
-
-~~~{swift}
-let formatter = NSNumberFormatter()
-formatter.numberStyle = NSNumberFormatterStyle.CurrencyStyle
-
-for identifier in ["en_US", "fr_FR", "ja_JP"] {
- formatter.locale = NSLocale(localeIdentifier: identifier)
- print("\(identifier) \(formatter.stringFromNumber(1234.5678))")
-}
-~~~
-
-~~~{objective-c}
-NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
-[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
-
-for (NSString *identifier in @[@"en_US", @"fr_FR", @"ja_JP"]) {
- numberFormatter.locale = [NSLocale localeWithLocaleIdentifier:identifier];
- NSLog(@"%@: %@", identifier, [numberFormatter stringFromNumber:@(1234.5678)]);
-}
-~~~
-
-| Locale | Formatted Number |
-|---------|---------------------|
-| `en_US` | $1,234.57 |
-| `fr_FR` | 1 234,57 € |
-| `ja_JP` | ¥1,235 |
-
-> All of those settings can be overridden on an individual basis, but for most apps, the best strategy would be deferring to the locale's default settings.
-
-### Rounding & Significant Digits
-
-In order to prevent numbers from getting annoyingly pedantic (_"thirty-two point three three, repeating, of course..."_), make sure to get a handle on `NSNumberFormatter`'s rounding behavior.
-
-The easiest way to do this, would be to set the `usesSignificantDigits` property to `false`, and then set minimum and maximum number of significant digits appropriately. For example, a number formatter used for approximate distances in directions, would do well with significant digits to the tenths place for miles or kilometers, but only the ones place for feet or meters.
-
-> For anything more advanced, an `NSDecimalNumberHandler` object can be passed as the `roundingBehavior` property of a number formatter.
-
-## NSDateFormatter
-
-`NSDateFormatter` is the be all and end all of getting textual representations of both dates and times.
-
-### Date & Time Styles
-
-The most important properties for an `NSDateFormatter` object are its `dateStyle` and `timeStyle`. Like `NSNumberFormatter numberStyle`, these styles provide common preset configurations for common formats. In this case, the various formats are distinguished by their specificity (more specific = longer).
-
-Both properties share a single set of `enum` values:
-
-#### `NSDateFormatterStyle`
-
-
-
-
-
Style
-
Description
-
Examples
-
-
-
-
Date
-
Time
-
-
-
-
-
NoStyle
-
Specifies no style.
-
-
-
-
-
ShortStyle
-
Specifies a short style, typically numeric only.
-
11/23/37
-
3:30pm
-
-
-
MediumStyle
-
Specifies a medium style, typically with abbreviated text.
-
Nov 23, 1937
-
3:30:32pm
-
-
-
LongStyle
-
Specifies a long style, typically with full text.
-
November 23, 1937
-
3:30:32pm
-
-
-
FullStyle
-
Specifies a full style with complete details.
-
Tuesday, April 12, 1952 AD
-
3:30:42pm PST
-
-
-
-
-`dateStyle` and `timeStyle` are set independently. For example, to display just the time, an `NSDateFormatter` would be configured with a `dateStyle` of `NoStyle`:
-
-~~~{swift}
-let formatter = NSDateFormatter()
-formatter.dateStyle = .NoStyle
-formatter.timeStyle = .MediumStyle
-
-let string = formatter.stringFromDate(NSDate())
-// 10:42:21am
-~~~
-
-~~~{objective-c}
-NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
-[formatter setDateStyle:NSDateFormatterNoStyle];
-[formatter setTimeStyle:NSDateFormatterMediumStyle];
-
-NSLog(@"%@", [formatter stringFromDate:[NSDate date]]);
-// 12:11:19pm
-~~~
-
-Whereas setting both to `LongStyle` yields the following:
-
-~~~{swift}
-let formatter = NSDateFormatter()
-formatter.dateStyle = .LongStyle
-formatter.timeStyle = .LongStyle
-
-let string = formatter.stringFromDate(NSDate())
-// Monday June 30, 2014 10:42:21am PST
-~~~
-
-~~~{objective-c}
-NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
-[formatter setDateStyle:NSDateFormatterLongStyle];
-[formatter setTimeStyle:NSDateFormatterLongStyle];
-
-NSLog(@"%@", [formatter stringFromDate:[NSDate date]]);
-// Monday, November 11, 2013 12:11:19pm PST
-~~~
-
-As you might expect, each aspect of the date format can alternatively be configured individually, a la carte. For any aspiring time wizards `NSDateFormatter` has a bevy of different knobs and switches to play with.
-
-### Relative Formatting
-
-As of iOS 4 / OS X Snow Leopard, `NSDateFormatter` supports relative date formatting for certain locales with the `doesRelativeDateFormatting` property. Setting this to `true` would format the date of `NSDate()` to "Today".
-
-## NSByteCounterFormatter
-
-For apps that work with files, data in memory, or information downloaded from a network, `NSByteCounterFormatter` is a must-have. All of the great information unit formatting behavior seen in Finder and other OS X apps is available with `NSByteCounterFormatter`, without any additional configuration.
-
-`NSByteCounterFormatter` takes a raw number of bytes and formats it into more meaningful units. For example, rather than bombarding a user with a ridiculous quantity, like "8475891734 bytes", a formatter can make a more useful approximation of "8.48 GB":
-
-~~~{swift}
-let formatter = NSByteCountFormatter()
-let byteCount = 8475891734
-let string = formatter.stringFromByteCount(Int64(byteCount))
-// 8.48 GB
-~~~
-~~~{objective-c}
-NSByteCountFormatter *formatter = [[NSByteCountFormatter alloc] init];
-long long byteCount = 8475891734;
-NSLog(@"%@", [formatter stringFromByteCount:byteCount]);
-// 8.48 GB
-~~~
-
-By default, specifying a `0` byte count will yield a localized string like "Zero KB". For a more consistent format, set `allowsNonnumericFormatting` to `false`:
-
-~~~{swift}
-let formatter = NSByteCountFormatter()
-let byteCount = 0
-
-formatter.stringFromByteCount(Int64(byteCount))
-// Zero KB
-
-formatter.allowsNonnumericFormatting = false
-formatter.stringFromByteCount(Int64(byteCount))
-// 0 bytes
-~~~
-~~~{objective-c}
-NSByteCountFormatter *formatter = [[NSByteCountFormatter alloc] init];
-long long byteCount = 0;
-
-NSLog(@"%@", [formatter stringFromByteCount:byteCount]);
-// Zero KB
-
-formatter.allowsNonnumericFormatting = NO;
-NSLog(@"%@", [formatter stringFromByteCount:byteCount]);
-// 0 bytes
-~~~
-
-### Count Style
-
-One might think that dealing with bytes in code (which is, you know, _a medium of bytes_) would be a piece of cake, but in reality, even determining how many bytes are in a kilobyte remains a contentious and confusing matter. ([Obligatory XKCD link](http://xkcd.com/394/))
-
-In [SI](http://en.wikipedia.org/wiki/International_System_of_Units), the "kilo" prefix multiplies the base quantity by 1000 (i.e. 1km == 1000 m). However, being based on a binary number system, a more convenient convention has been to make a kilobyte equal to 210 bytes instead (i.e. 1KB == 1024 bytes). While the 2% difference is negligible at lower quantities, this confusion has significant implications when, for example, determining how much space is available on a 1TB drive (either 1000GB or 1024 GB).
-
-To complicate matters further, this binary prefix was codified into the [Kibibyte](http://en.wikipedia.org/wiki/Kibibyte) standard by the [IEC](http://en.wikipedia.org/wiki/International_Electrotechnical_Commission) in 1998... which is summarily ignored by the [JEDEC](http://en.wikipedia.org/wiki/JEDEC_memory_standards#Unit_prefixes_for_semiconductor_storage_capacity), the trade and engineering standardization organization representing the actual manufacturers of storage media. The result is that one can represent information as either `1kB`, `1KB`, or `1KiB`. ([Another obligatory XKCD link](http://xkcd.com/927/))
-
-Rather than get caught up in all of this, simply use the most appropriate count style for your particular use case:
-
-#### NSByteCountFormatterCountStyle
-
-| `File` | Specifies display of file byte counts. The actual behavior for this is platform-specific; on OS X Mountain Lion, this uses the binary style, but that may change over time. |
-| `Memory` | Specifies display of memory byte counts. The actual behavior for this is platform-specific; on OS X Mountain Lion, this uses the binary style, but that may change over time. |
-
-In most cases, it is better to use `File` or `Memory`, however decimal or binary byte counts can be explicitly specified with either of the following values:
-
-| `Decimal` | Causes 1000 bytes to be shown as 1 KB. |
-| `Binary` | Causes 1024 bytes to be shown as 1 KB. |
-
-## Date & Time Interval Formatters
-
-`NSDateFormatter` is great for points in time, but when it comes to dealing with date or time ranges, Foundation was without any particularly great options. That is, until the introduction of `NSDateComponentsFormatter` & `NSDateIntervalFormatter`.
-
-## NSDateComponentsFormatter
-
-As the name implies, `NSDateComponentsFormatter` works with `NSDateComponents`, which was covered in [a previous NSHipster article](http://nshipster.com/nsdatecomponents/). An `NSDateComponents` object is a container for representing a combination of discrete calendar quantities, such as "1 day and 2 hours". `NSDateComponentsFormatter` provides localized representations of `NSDateComponents` objects in several different formats:
-
-~~~{swift}
-let formatter = NSDateComponentsFormatter()
-formatter.unitsStyle = .Full
-
-let components = NSDateComponents()
-components.day = 1
-components.hour = 2
-
-let string = formatter.stringFromDateComponents(components)
-// 1 day, 2 hours
-~~~
-~~~{objective-c}
-NSDateComponentsFormatter *formatter = [[NSDateComponentsFormatter alloc] init];
-formatter.unitsStyle = NSDateComponentsFormatterUnitsStyleFull;
-
-NSDateComponents *components = [[NSDateComponents alloc] init];
-components.day = 1;
-components.hour = 2;
-
-NSLog(@"%@", [formatter stringFromDateComponents:components]);
-// 1 day, 2 hours
-~~~
-
-#### `NSDateComponentsFormatterUnitsStyle`
-
-| Unit Style | Example |
-|-----------------|-------------------------------------------------------------------|
-| `Positional` | "1:10" |
-| `Abbreviated` | "1h 10m" |
-| `Short` | "1hr 10min" |
-| `Full` | "1 hour, 10 minutes" |
-| `SpellOut` | "One hour, ten minutes" |
-
-### NSDateIntervalFormatter
-
-Like `NSDateComponentsFormatter`, `NSDateIntervalFormatter` deals with ranges of time, but specifically for time intervals between a start and end date:
-
-~~~{swift}
-let formatter = NSDateIntervalFormatter()
-formatter.dateStyle = .NoStyle
-formatter.timeStyle = .ShortStyle
-
-let fromDate = NSDate()
-let toDate = fromDate.dateByAddingTimeInterval(10000)
-
-let string = formatter.stringFromDate(fromDate, toDate: toDate)
-// 5:49 - 8:36 PM
-~~~
-~~~{objective-c}
-NSDateIntervalFormatter *formatter = [[NSDateIntervalFormatter alloc] init];
-formatter.dateStyle = NSDateIntervalFormatterNoStyle;
-formatter.timeStyle = NSDateIntervalFormatterShortStyle;
-
-NSDate *fromDate = [NSDate date];
-NSDate *toDate = [fromDate dateByAddingTimeInterval:10000];
-
-NSLog(@"%@", [formatter stringFromDate:fromDate toDate:toDate]);
-// 5:49 - 8:36 PM
-~~~
-
-#### `NSDateIntervalFormatterStyle`
-
-| Formatter Style | Time Output | Date Output |
-|---------------------|-------------------------------------|-------------------------------|
-| `NoStyle` | | |
-| `ShortStyle` | 5:51 AM - 7:37 PM | 6/30/14 - 7/11/14 |
-| `MediumStyle` | 5:51:49 AM - 7:38:29 PM | Jun 30, 2014 - Jul 11, 2014 |
-| `LongStyle` | 6:02:54 AM GMT-8 - 7:49:34 PM GMT-8 | June 30, 2014 - July 11, 2014 |
-| `FullStyle` | 6:03:28 PM Pacific Standard Time - 7:50:08 PM Pacific Standard Time | Monday, June 30, 2014 - Friday, July 11, 2014 |
-
-`NSDateIntervalFormatter` and `NSDateComponentsFormatter` are useful for displaying regular and pre-defined ranges of times such as the such as the opening hours of a business, or frequency or duration of calendar events.
-
-> In the case of displaying business hours, such as "Mon – Fri: 8:00 AM – 10:00 PM", use the `weekdaySymbols` of an `NSDateFormatter` to get the localized names of the days of the week.
-
-## Mass, Length, & Energy Formatters
-
-Prior to iOS 8 / OS X Yosemite, working with physical quantities was left as an exercise to the developer. However, with the introduction of HealthKit, this functionality is now provided in the standard library.
-
-### NSMassFormatter
-
-Although the fundamental unit of physical existence, mass is pretty much relegated to tracking the weight of users in HealthKit.
-
-> Yes, mass and weight are different, but this is programming, not science class, so stop being pedantic.
-
-~~~{swift}
-let massFormatter = NSMassFormatter()
-let kilograms = 60.0
-print(massFormatter.stringFromKilograms(kilograms)) // "132 lb"
-~~~
-~~~{objective-c}
-NSMassFormatter *massFormatter = [[NSMassFormatter alloc] init];
-double kilograms = 60;
-NSLog(@"%@", [massFormatter stringFromKilograms:kilograms]); // "132 lb"
-~~~
-
-### NSLengthFormatter
-
-`NSLengthFormatter` can be thought of as a more useful version of `MKDistanceFormatter`, with more unit options and formatting options.
-
-~~~{swift}
-let lengthFormatter = NSLengthFormatter()
-let meters = 5_000.0
-print(lengthFormatter.stringFromMeters(meters)) // "3.107 mi"
-~~~
-~~~{objective-c}
-NSLengthFormatter *lengthFormatter = [[NSLengthFormatter alloc] init];
-double meters = 5000;
-NSLog(@"%@", [lengthFormatter stringFromMeters:meters]); // "3.107 mi"
-~~~
-
-### NSEnergyFormatter
-
-Rounding out the new `NSFormatter` subclasses added for HealthKit is `NSEnergyFormatter`, which formats energy in Joules, the raw unit of work for exercises, and Calories, which is used when working with nutrition information.
-
-~~~{swift}
-let energyFormatter = NSEnergyFormatter()
-energyFormatter.forFoodEnergyUse = true
-
-let joules = 10_000.0
-print(energyFormatter.stringFromJoules(joules)) // "2.39 Cal"
-~~~
-~~~{objective-c}
-NSEnergyFormatter *energyFormatter = [[NSEnergyFormatter alloc] init];
-energyFormatter.forFoodEnergyUse = YES;
-
-double joules = 10000;
-NSLog(@"%@", [energyFormatter stringFromJoules:joules]); // "2.39 Cal"
-~~~
-
----
-
-## Re-Using Formatter Instances
-
-Perhaps the most critical detail to keep in mind when using formatters is that they are _extremely_ expensive to create. Even just an `alloc init` of an `NSNumberFormatter` in a tight loop is enough to bring an app to its knees.
-
-Therefore, it's strongly recommended that formatters be created once, and re-used as much as possible.
-
-If it's just a single method using a particular formatter, a static instance is a good strategy:
-
-~~~{swift}
-// a nested struct can have a static property,
-// created only the first time it's encountered.
-func fooWithNumber(number: NSNumber) {
- struct NumberFormatter {
- static let formatter: NSNumberFormatter = {
- let formatter = NSNumberFormatter()
- formatter.numberStyle = .DecimalStyle
- return formatter
- }()
- }
-
- let string = NumberFormatter.formatter.stringFromNumber(number)
- // ...
-}
-~~~
-
-~~~{objective-c}
-// dispatch_once guarantees that the specified block is called
-// only the first time it's encountered.
-- (void)fooWithNumber:(NSNumber *)number {
- static NSNumberFormatter *_numberFormatter = nil;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- _numberFormatter = [[NSNumberFormatter alloc] init];
- [_numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
- });
-
- NSString *string = [_numberFormatter stringFromNumber:number];
-
- // ...
-}
-~~~
-
-If the formatter is used across several methods in the same class, that static instance can be refactored into a singleton method in Objective-C or a static type property in Swift:
-
-~~~{swift}
-static let numberFormatter: NSNumberFormatter = {
- let formatter = NSNumberFormatter()
- formatter.numberStyle = .DecimalStyle
- return formatter
-}()
-~~~
-
-~~~{objective-c}
-+ (NSNumberFormatter *)numberFormatter {
- static NSNumberFormatter *_numberFormatter = nil;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- _numberFormatter = [[NSNumberFormatter alloc] init];
- [_numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
- });
-
- return _numberFormatter;
-}
-~~~
-
-If the same formatter is privately implemented across several classes, one could either expose it publicly in one of the classes, or implement the static singleton method in a category on `NSNumberFormatter`.
-
-> Prior to iOS 7 and OS X Mavericks, `NSDateFormatter` & `NSNumberFormatter` were not thread safe. Under these circumstances, the safest way to reuse formatter instances was with a thread dictionary:
-
-~~~{objective-c}
-NSMutableDictionary *threadDictionary = [[NSThread currentThread] threadDictionary];
-NSDateFormatter *dateFormatter = threadDictionary[@"dateFormatter"];
-if (!dateFormatter) {
- dateFormatter = [[NSDateFormatter alloc] init];
- dateFormatter.locale = [NSLocale currentLocale];
- dateFormatter.dateStyle = NSDateFormatterLongStyle;
- dateFormatter.timeStyle = NSDateFormatterShortStyle;
- threadDictionary[@"dateFormatter"] = dateFormatter;
-}
-
-return dateFormatter;
-~~~
-
-## Formatter Context
-
-Another addition to formatters in iOS 8 & OS X Yosemite is the idea of formatter _contexts_. This allows the formatted output to be correctly integrated into the localized string. The most salient application of this is the letter casing of formatted output at different parts of a sentence in western locales, such as English. For example, when appearing at the beginning of a sentence or by itself, the first letter of formatted output would be capitalized, whereas it would be lowercase in the middle of a sentence.
-
-A `context` property is available for `NSDateFormatter`, `NSNumberFormatter`, `NSDateComponentsFormatter`, and `NSByteCountFormatter`, with the following values:
-
-| Formatting Context | Output |
-|-------------------------|-----------------|
-| `Standalone` | "About 2 hours" |
-| `ListItem` | "About 2 hours" |
-| `BeginningOfSentence` | "About 2 hours" |
-| `MiddleOfSentence` | "about 2 hours" |
-| `Dynamic` | _(Depends)_ |
-
-In cases where localizations may change the position of formatted information within a string, the `Dynamic` value will automatically change depending on where it appears in the text.
-
-- - -
-
-## Third Party Formatters
-
-An article on `NSFormatter` would be remiss without mention of some of the third party subclasses that have made themselves mainstays of everyday app development: [ISO8601DateFormatter](http://boredzo.org/iso8601dateformatter/) & [FormatterKit](https://github.com/mattt/FormatterKit)
-
-### ISO8601DateFormatter
-
-Created by [Peter Hosey](https://twitter.com/boredzo), [ISO8601DateFormatter](https://github.com/boredzo/iso-8601-date-formatter) has become the de facto way of dealing with [ISO 8601 timestamps](http://en.wikipedia.org/wiki/ISO_8601), used as the interchange format for dates by webservices ([Yet another obligatory XKCD link](http://xkcd.com/1179/)).
-
-Although Apple provides [official recommendations on parsing internet dates](https://developer.apple.com/library/ios/qa/qa1480/_index.html), the reality of formatting quirks across makes the suggested `NSDateFormatter`-with-`en_US_POSIX`-locale approach untenable for real-world usage. `ISO8601DateFormatter` offers a simple, robust interface for dealing with timestamps:
-
-~~~{swift}
-let formatter = ISO8601DateFormatter()
-let timestamp = "2014-06-30T08:21:56+08:00"
-let date = formatter.dateFromString(timestamp)
-~~~
-
-> Although not an `NSFormatter` subclass, [TransformerKit](https://github.com/mattt/TransformerKit) offers an extremely performant alternative for parsing and formatting both ISO 8601 and RFC 2822 timestamps.
-
-### FormatterKit
-
-[FormatterKit](https://github.com/mattt/FormatterKit) has great examples of `NSFormatter` subclasses for use cases not currently covered by built-in classes, such as localized addresses, arrays, colors, locations, and ordinal numbers, and URL requests. It also boasts localization in 23 different languages, making it well-suited to apps serving every major market.
-
-#### TTTAddressFormatter
-
-~~~{swift}
-let formatter = TTTAddressFormatter()
-formatter.locale = NSLocale(localeIdentifier: "en_GB")
-
-let street = "221b Baker St"
-let locality = "Paddington"
-let region = "Greater London"
-let postalCode = "NW1 6XE"
-let country = "United Kingdom"
-
-let string = formatter.stringFromAddressWithStreet(street: street, locality: locality, region: region, postalCode: postalCode, country: country)
-// 221b Baker St / Paddington / Greater London / NW1 6XE / United Kingdom
-~~~
-~~~{objective-c}
-TTTAddressFormatter *formatter = [[TTTAddressFormatter alloc] init];
-[formatter setLocale:[NSLocale localeWithLocaleIdentifier:@"en_GB"]];
-
-NSString *street = @"221b Baker St";
-NSString *locality = @"Paddington";
-NSString *region = @"Greater London";
-NSString *postalCode = @"NW1 6XE";
-NSString *country = @"United Kingdom";
-
-NSLog(@"%@", [formatter stringFromAddressWithStreet:street locality:locality region:region postalCode:postalCode country:country]);
-// 221b Baker St / Paddington / Greater London / NW1 6XE / United Kingdom
-~~~
-
-#### TTTArrayFormatter
-
-~~~{swift}
-let formatter = TTTArrayFormatter()
-formatter.usesAbbreviatedConjunction = true // Use '&' instead of 'and'
-formatter.usesSerialDelimiter = true // Omit Oxford Comma
-
-let array = ["Russel", "Spinoza", "Rawls"]
-let string = formatter.stringFromArray(array)
-// "Russell, Spinoza & Rawls"
-~~~
-~~~{objective-c}
-TTTArrayFormatter *formatter = [[TTTArrayFormatter alloc] init];
-formatter.usesAbbreviatedConjunction = YES; // Use '&' instead of 'and'
-formatter.usesSerialDelimiter = YES; // Omit Oxford Comma
-
-NSArray *array = @[@"Russel", @"Spinoza", @"Rawls"];
-NSLog(@"%@", [formatter stringFromArray:array]);
-// "Russell, Spinoza & Rawls"
-~~~
-
-#### TTTColorFormatter
-
-~~~{swift}
-let formatter = TTTColorFormatter()
-let color = UIColor.orangeColor()
-let hex = formatter.hexadecimalStringFromColor(color);
-// #ffa500
-~~~
-~~~{objective-c}
-TTTColorFormatter *formatter = [[TTTColorFormatter alloc] init];
-UIColor *color = [UIColor orangeColor];
-NSLog(@"%@", [formatter hexadecimalStringFromColor:color]);
-// #ffa500
-~~~
-
-#### TTTLocationFormatter
-
-~~~{swift}
-let formatter = TTTLocationFormatter()
-formatter.numberFormatter.maximumSignificantDigits = 4
-formatter.bearingStyle = TTTBearingAbbreviationWordStyle
-formatter.unitSystem = TTTImperialSystem
-
-let pittsburgh = CLLocation(latitude: 40.4405556, longitude: -79.9961111)
-let austin = CLLocation(latitude: 30.2669444, longitude: -97.7427778)
-let string = formatter.stringFromDistanceAndBearingFromLocation(pittsburgh, toLocation: austin)
-// "1,218 miles SW"
-~~~
-~~~{objective-c}
-TTTLocationFormatter *formatter = [[TTTLocationFormatter alloc] init];
-formatter.numberFormatter.maximumSignificantDigits = 4;
-formatter.bearingStyle = TTTBearingAbbreviationWordStyle;
-formatter.unitSystem = TTTImperialSystem;
-
-CLLocation *pittsburgh = [[CLLocation alloc] initWithLatitude:40.4405556 longitude:-79.9961111];
-CLLocation *austin = [[CLLocation alloc] initWithLatitude:30.2669444 longitude:-97.7427778];
-NSLog(@"%@", [formatter stringFromDistanceAndBearingFromLocation:pittsburgh toLocation:austin]);
-// "1,218 miles SW"
-~~~
-
-#### TTTOrdinalNumberFormatter
-
-~~~{swift}
-let formatter = TTTOrdinalNumberFormatter()
-formatter.locale = NSLocale(localeIdentifier: "fr_FR")
-formatter.grammaticalGender = TTTOrdinalNumberFormatterMaleGender
-let string = NSString(format: NSLocalizedString("You came in %@ place", comment: ""), formatter.stringFromNumber(2))
-// "Vous êtes arrivé à la 2e place!"
-~~~
-~~~{objective-c}
-TTTOrdinalNumberFormatter *formatter = [[TTTOrdinalNumberFormatter alloc] init];
-[formatter setLocale:[NSLocale localeWithLocaleIdentifier:@"fr_FR"]];
-[formatter setGrammaticalGender:TTTOrdinalNumberFormatterMaleGender];
-NSLog(@"%@", [NSString stringWithFormat:NSLocalizedString(@"You came in %@ place!", nil), [formatter stringFromNumber:@2]]);
-// "Vous êtes arrivé à la 2e place!"
-~~~
-
-#### TTTURLRequestFormatter
-
-~~~{swift}
-let request = NSMutableURLRequest(URL: NSURL(string: "http://nshipster.com")!)
-request.HTTPMethod = "GET"
-request.addValue("text/html", forHTTPHeaderField: "Accept")
-
-let command = TTTURLRequestFormatter.cURLCommandFromURLRequest(request)
-// curl -X GET "https://nshipster.com/" -H "Accept: text/html"
-~~~
-~~~{objective-c}
-NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://www.example.com/"]];
-[request setHTTPMethod:@"GET"];
-[request addValue:@"text/html" forHTTPHeaderField:@"Accept"];
-NSLog(@"%@", [TTTURLRequestFormatter cURLCommandFromURLRequest:request]);
-// curl -X GET "https://nshipster.com/" -H "Accept: text/html"
-~~~
-
-* * *
-
-If your app deals in numbers or dates (or time intervals or bytes or distance or length or energy or mass), `NSFormatter` is indispensable. Actually, if your app doesn't… then what _does_ it do, exactly?
-
-Presenting useful information to users is as much about content as presentation. Invest in learning all of the secrets of `NSNumberFormatter`, `NSDateFormatter`, and the rest of the Foundation formatter crew to get everything exactly how you want them.
-
-And if you find yourself with formatting logic scattered across your app, consider creating your own `NSFormatter` subclass to consolidate all of that business logic in one place.
diff --git a/2013-11-18-filemanager.md b/2013-11-18-filemanager.md
new file mode 100644
index 00000000..4f845b1f
--- /dev/null
+++ b/2013-11-18-filemanager.md
@@ -0,0 +1,460 @@
+---
+title: FileManager
+author: Mattt
+category: Cocoa
+tags: nshipster
+excerpt: >-
+ `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.
+revisions:
+ "2013-11-18": Original publication
+ "2018-10-29": Updated for Swift 4.2
+status:
+ swift: 4.2
+ reviewed: October 29, 2018
+---
+
+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(_:)`](/numericcast).
+
+But let's take the metaphor a step further,
+and imagine that instead of commanding a single robot to
+([`sudo`](https://xkcd.com/149/))
+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`](https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory).
+These determine what kind of standard directory you're looking for,
+like "Documents" or "Caches".
+
+The second parameter passes a
+[`FileManager.SearchPathDomainMask`](https://developer.apple.com/documentation/foundation/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.
+
+```swift
+let documentsDirectoryURL =
+ try FileManager.default.url(for: .documentDirectory,
+ in: .userDomainMask,
+ appropriateFor: nil,
+ create: false)
+```
+
+```objc
+NSFileManager *fileManager = [NSFileManager defaultManager];
+NSString *documentsPath =
+ [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
+NSString *filePath = [documentsPath stringByAppendingPathComponent:@"file.txt"];
+```
+
+{% info %}
+
+Files and directories can have alternate names from what's encoded by the path.
+
+For example, most macOS system directories are localized;
+although a user's photos located at `~/Photos`,
+the `~/Photos/.localized` file can change how the folder is named in Finder
+and Open / Save panels.
+An app can also provide locale-specific names for itself
+and directories it creates.
+
+Another example of this is when files
+are configured to hide their file extension.
+(You've probably encountered this at some point,
+with some degree of bewilderment).
+
+Long story short, when displaying the name of a file or directory to the user,
+don't simply take the last path component.
+Instead, call the method `displayName(atPath:)`:
+
+```swift
+let directoryURL: URL = <#/path/to/directory#>
+
+// Bad
+let filename = directoryURL.pathComponents.last
+
+// Good
+let filename = FileManager.default.displayName(atPath: url.path)
+```
+
+{% endinfo %}
+
+## 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:
+
+```swift
+let fileURL: URL = <#/path/to/file#>
+let fileExists = FileManager.default.fileExists(atPath: fileURL.path)
+```
+
+```objc
+NSURL *fileURL = <#/path/to/file#>;
+NSFileManager *fileManager = [NSFileManager defaultManager];
+BOOL fileExists = [fileManager fileExistsAtPath:[fileURL path]];
+```
+
+## Getting Information About a File
+
+The file system stores various pieces of metadata
+about each file and directory in the system.
+You can access them using the `FileManager` method `attributesOfItem(atPath:)`.
+The resulting dictionary contains attributes keyed by `FileAttributeKey` values,
+including `.creationDate`:
+
+```swift
+let fileURL: URL = <#/path/to/file#>
+let attributes =
+ FileManager.default.attributesOfItem(atPath: fileURL.path)
+let creationDate = attributes[.creationDate]
+```
+
+```objc
+NSURL *fileURL = <#/path/to/file#>;
+NSFileManager *fileManager = [NSFileManager defaultManager];
+
+NSError *error = nil;
+NSDictionary *attributes = [fileManager attributesOfItemAtPath:[fileURL path]
+ error:&error];
+NSDate *creationDate = attributes[NSFileCreationDate];
+```
+
+{% info %}
+File attributes used to be keyed by string constants,
+which made them hard to discover through autocompletion or documentation.
+Fortunately, it's now easy to
+[see everything that's available](https://developer.apple.com/documentation/foundation/fileattributekey).
+{% endinfo %}
+
+## 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.
+
+```swift
+let directoryURL: URL = <#/path/to/directory#>
+let contents =
+ try FileManager.default.contentsOfDirectory(at: directoryURL,
+ includingPropertiesForKeys: nil,
+ options: [.skipsHiddenFiles])
+for file in contents {
+ <#...#>
+}
+```
+
+```objc
+NSFileManager *fileManager = [NSFileManager defaultManager];
+NSURL *bundleURL = [[NSBundle mainBundle] bundleURL];
+NSArray *contents = [fileManager contentsOfDirectoryAtURL:bundleURL
+ includingPropertiesForKeys:@[]
+ options:NSDirectoryEnumerationSkipsHiddenFiles
+ error:nil];
+
+NSPredicate *predicate = [NSPredicate predicateWithFormat:@"pathExtension == 'png'"];
+for (NSURL *fileURL in [contents filteredArrayUsingPredicate:predicate]) {
+ // Enumerate each .png file in directory
+}
+```
+
+### Recursively Enumerating Files In A Directory
+
+If you want to go through each subdirectory at a particular location recursively,
+you can do so by creating a `FileManager.DirectoryEnumerator` object
+with the `enumerator(atPath:)` method:
+
+```swift
+let directoryURL: URL = <#/path/to/directory#>
+
+if let enumerator =
+ FileManager.default.enumerator(atPath: directoryURL.path)
+{
+ for case let path as String in enumerator {
+ // Skip entries with '_' prefix, for example
+ if path.hasPrefix("_") {
+ enumerator.skipDescendants()
+ }
+ }
+}
+```
+
+```objc
+NSFileManager *fileManager = [NSFileManager defaultManager];
+NSURL *bundleURL = [[NSBundle mainBundle] bundleURL];
+NSDirectoryEnumerator *enumerator = [fileManager enumeratorAtURL:bundleURL
+ includingPropertiesForKeys:@[NSURLNameKey, NSURLIsDirectoryKey]
+ options:NSDirectoryEnumerationSkipsHiddenFiles
+ errorHandler:^BOOL(NSURL *url, NSError *error)
+{
+ if (error) {
+ NSLog(@"[Error] %@ (%@)", error, url);
+ return NO;
+ }
+
+ return YES;
+}];
+
+NSMutableArray *mutableFileURLs = [NSMutableArray array];
+for (NSURL *fileURL in enumerator) {
+ NSString *filename;
+ [fileURL getResourceValue:&filename forKey:NSURLNameKey error:nil];
+
+ NSNumber *isDirectory;
+ [fileURL getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:nil];
+
+ // Skip directories with '_' prefix, for example
+ if ([filename hasPrefix:@"_"] && [isDirectory boolValue]) {
+ [enumerator skipDescendants];
+ continue;
+ }
+
+ if (![isDirectory boolValue]) {
+ [mutableFileURLs addObject:fileURL];
+ }
+}
+```
+
+## Creating a Directory
+
+To create a directory,
+call the method `createDirectory(at:withIntermediateDirectories:attributes:)`.
+In Unix parlance, setting the `withIntermediateDirectories` parameter to `true`
+is equivalent to passing the `-p` option to `mkdir`.
+
+```swift
+try FileManager.default.createDirectory(at: directoryURL,
+ withIntermediateDirectories: true,
+ attributes: nil)
+```
+
+```objc
+NSFileManager *fileManager = [NSFileManager defaultManager];
+NSString *documentsPath =
+ [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
+ NSUserDomainMask,
+ YES) firstObject];
+NSString *imagesPath = [documentsPath stringByAppendingPathComponent:@"images"];
+if (![fileManager fileExistsAtPath:imagesPath]) {
+ NSError *error = nil;
+ [fileManager createDirectoryAtPath:imagesPath
+ withIntermediateDirectories:NO
+ attributes:nil
+ error:&error];
+}
+```
+
+## Deleting a File or Directory
+
+If you want to delete a file or directory,
+call `removeItem(at:)`:
+
+```swift
+let fileURL: URL = <#/path/to/file#>
+try FileManager.default.removeItem(at: fileURL)
+```
+
+```objc
+NSFileManager *fileManager = [NSFileManager defaultManager];
+NSString *documentsPath =
+ [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
+ NSUserDomainMask,
+ YES) firstObject];
+NSString *filePath = [documentsPath stringByAppendingPathComponent:@"image.png"];
+NSError *error = nil;
+
+if (![fileManager removeItemAtPath:filePath error:&error]) {
+ NSLog(@"[Error] %@ (%@)", error, filePath);
+}
+```
+
+## FileManagerDelegate
+
+`FileManager` may optionally set a delegate
+to verify that it should perform a particular file operation.
+This is a convenient way to audit all file operations in your app,
+and a good place to factor out and centralize business logic,
+such as which files to protect from deletion.
+
+There are four operations covered by the
+[`FileManagerDelegate`](https://developer.apple.com/documentation/foundation/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.
+
+```swift
+class CustomFileManagerDelegate: NSObject, FileManagerDelegate {
+ func fileManager(_ fileManager: FileManager,
+ shouldRemoveItemAt URL: URL) -> Bool
+ {
+ // Don't delete PDF files
+ return URL.pathExtension != "pdf"
+ }
+}
+
+// Maintain strong references to fileManager and delegate
+let fileManager = FileManager()
+let delegate = CustomFileManagerDelegate()
+
+fileManager.delegate = delegate
+```
+
+```objc
+NSFileManager *fileManager = [[NSFileManager alloc] init];
+fileManager.delegate = delegate;
+
+NSURL *bundleURL = [[NSBundle mainBundle] bundleURL];
+NSArray *contents = [fileManager contentsOfDirectoryAtURL:bundleURL
+ includingPropertiesForKeys:@[]
+ options:NSDirectoryEnumerationSkipsHiddenFiles
+ error:nil];
+
+for (NSString *filePath in contents) {
+ [fileManager removeItemAtPath:filePath error:nil];
+}
+
+// CustomFileManagerDelegate.m
+
+#pragma mark - NSFileManagerDelegate
+
+- (BOOL)fileManager:(NSFileManager *)fileManager
+shouldRemoveItemAtURL:(NSURL *)URL
+{
+ return ![[[URL lastPathComponent] pathExtension] isEqualToString:@"pdf"];
+}
+```
+
+---
+
+When you write an app that interacts with a file system,
+you don't know if it's an HDD or SSD
+or if it's formatted with APFS or HFS+ or something else entirely.
+You don't even know where the disk is:
+it could be internal or in a mounted peripheral,
+it could be network-attached, or maybe floating around somewhere in the cloud.
+
+The best strategy for ensuring that things work
+across each of the various permutations
+is to work through `FileManager` and its related Foundation APIs.
diff --git a/2013-11-18-nsfilemanager.md b/2013-11-18-nsfilemanager.md
deleted file mode 100644
index f23274fb..00000000
--- a/2013-11-18-nsfilemanager.md
+++ /dev/null
@@ -1,227 +0,0 @@
----
-title: NSFileManager
-author: Mattt Thompson
-category: Cocoa
-tags: nshipster
-excerpt: "File systems are a complex topic, with decades of history, vestigial complexities, and idiosyncrasies, and is well outside the scope of a single article. And since most applications don't often interact with the file system much beyond simple file operations, one can get away with only knowing the basics."
-status:
- swift: t.b.c.
----
-
-`NSFileManager` is Foundation's high-level API for working with file systems. It abstracts Unix and Finder internals, providing a convenient way to create, read, move, copy, and delete files & directories on local or networked drives, as well as iCloud ubiquitous containers.
-
-File systems are a complex topic, with decades of history, vestigial complexities, and idiosyncrasies, and is well outside the scope of a single article. And since most applications don't often interact with the file system much beyond simple file operations, one can get away with only knowing the basics.
-
-What follows are some code samples for your copy-pasting pleasure. Use them as a foundation for understanding how to adjust parameters to your particular use case:
-
-## Common Tasks
-
-> Throughout the code samples is the magical incantation `NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)`. This may be tied with KVO as one of the worst APIs in Cocoa. Just know that this returns an array containing the user documents directory as the first object. Thank goodness for the inclusion of `NSArray -firstObject`.
-
-### Determining If A File Exists
-
-~~~{objective-c}
-NSFileManager *fileManager = [NSFileManager defaultManager];
-NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
-NSString *filePath = [documentsPath stringByAppendingPathComponent:@"file.txt"];
-BOOL fileExists = [fileManager fileExistsAtPath:filePath];
-~~~
-
-### Listing All Files In A Directory
-
-~~~{objective-c}
-NSFileManager *fileManager = [NSFileManager defaultManager];
-NSURL *bundleURL = [[NSBundle mainBundle] bundleURL];
-NSArray *contents = [fileManager contentsOfDirectoryAtURL:bundleURL
- includingPropertiesForKeys:@[]
- options:NSDirectoryEnumerationSkipsHiddenFiles
- error:nil];
-
-NSPredicate *predicate = [NSPredicate predicateWithFormat:@"pathExtension == 'png'"];
-for (NSURL *fileURL in [contents filteredArrayUsingPredicate:predicate]) {
- // Enumerate each .png file in directory
-}
-~~~
-
-## Recursively Enumerating Files In A Directory
-
-~~~{objective-c}
-NSFileManager *fileManager = [NSFileManager defaultManager];
-NSURL *bundleURL = [[NSBundle mainBundle] bundleURL];
-NSDirectoryEnumerator *enumerator = [fileManager enumeratorAtURL:bundleURL
- includingPropertiesForKeys:@[NSURLNameKey, NSURLIsDirectoryKey]
- options:NSDirectoryEnumerationSkipsHiddenFiles
- errorHandler:^BOOL(NSURL *url, NSError *error)
-{
- if (error) {
- NSLog(@"[Error] %@ (%@)", error, url);
- return NO;
- }
-
- return YES;
-}];
-
-NSMutableArray *mutableFileURLs = [NSMutableArray array];
-for (NSURL *fileURL in enumerator) {
- NSString *filename;
- [fileURL getResourceValue:&filename forKey:NSURLNameKey error:nil];
-
- NSNumber *isDirectory;
- [fileURL getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:nil];
-
- // Skip directories with '_' prefix, for example
- if ([filename hasPrefix:@"_"] && [isDirectory boolValue]) {
- [enumerator skipDescendants];
- continue;
- }
-
- if (![isDirectory boolValue]) {
- [mutableFileURLs addObject:fileURL];
- }
-}
-~~~
-
-### Creating a Directory
-
-~~~{objective-c}
-NSFileManager *fileManager = [NSFileManager defaultManager];
-NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
-NSString *imagesPath = [documentsPath stringByAppendingPathComponent:@"images"];
-if (![fileManager fileExistsAtPath:imagesPath]) {
- [fileManager createDirectoryAtPath:imagesPath withIntermediateDirectories:NO attributes:nil error:nil];
-}
-~~~
-
-### Deleting a File
-
-~~~{objective-c}
-NSFileManager *fileManager = [NSFileManager defaultManager];
-NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
-NSString *filePath = [documentsPath stringByAppendingPathComponent:@"image.png"];
-NSError *error = nil;
-
-if (![fileManager removeItemAtPath:filePath error:&error]) {
- NSLog(@"[Error] %@ (%@)", error, filePath);
-}
-~~~
-
-### Determining the Creation Date of a File
-
-~~~{objective-c}
-NSFileManager *fileManager = [NSFileManager defaultManager];
-NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
-NSString *filePath = [documentsPath stringByAppendingPathComponent:@"Document.pages"];
-
-NSDate *creationDate = nil;
-if ([fileManager fileExistsAtPath:filePath]) {
- NSDictionary *attributes = [fileManager attributesOfItemAtPath:filePath error:nil];
- creationDate = attributes[NSFileCreationDate];
-}
-~~~
-
-There are a number of file attributes that are made accessible through `NSFileManager`, which can be fetched with `-attributesOfItemAtPath:error:`, and other methods:
-
-#### File Attribute Keys
-
-> - `NSFileAppendOnly`: The key in a file attribute dictionary whose value indicates whether the file is read-only.
-> - `NSFileBusy`: The key in a file attribute dictionary whose value indicates whether the file is busy.
-> - `NSFileCreationDate`: The key in a file attribute dictionary whose value indicates the file's creation date.
-> - `NSFileOwnerAccountName`: The key in a file attribute dictionary whose value indicates the name of the file's owner.
-> - `NSFileGroupOwnerAccountName`: The key in a file attribute dictionary whose value indicates the group name of the file's owner.
-> - `NSFileDeviceIdentifier`: The key in a file attribute dictionary whose value indicates the identifier for the device on which the file resides.
-> - `NSFileExtensionHidden`: The key in a file attribute dictionary whose value indicates whether the file's extension is hidden.
-> - `NSFileGroupOwnerAccountID`: The key in a file attribute dictionary whose value indicates the file's group ID.
-> - `NSFileHFSCreatorCode`: The key in a file attribute dictionary whose value indicates the file's HFS creator code.
-> - `NSFileHFSTypeCode`: The key in a file attribute dictionary whose value indicates the file's HFS type code.
-> - `NSFileImmutable`: The key in a file attribute dictionary whose value indicates whether the file is mutable.
-> - `NSFileModificationDate`: The key in a file attribute dictionary whose value indicates the file's last modified date.
-> - `NSFileOwnerAccountID`: The key in a file attribute dictionary whose value indicates the file's owner's account ID.
-> - `NSFilePosixPermissions`: The key in a file attribute dictionary whose value indicates the file's Posix permissions.
-> - `NSFileReferenceCount`: The key in a file attribute dictionary whose value indicates the file's reference count.
-> - `NSFileSize`: The key in a file attribute dictionary whose value indicates the file's size in bytes.
-> - `NSFileSystemFileNumber`: The key in a file attribute dictionary whose value indicates the file's filesystem file number.
-> - `NSFileType`: The key in a file attribute dictionary whose value indicates the file's type.
-
-
-> - `NSDirectoryEnumerationSkipsSubdirectoryDescendants`: Perform a shallow enumeration; do not descend into directories.
-> - `NSDirectoryEnumerationSkipsPackageDescendants`: Do not descend into packages.
-> - `NSDirectoryEnumerationSkipsHiddenFiles`: Do not enumerate hidden files.
-
-## NSFileManagerDelegate
-
-`NSFileManager` may optionally set a delegate to verify that it should perform a particular file operation. This allows the business logic of, for instance, which files to protect from deletion, to be factored out of the controller.
-
-There are four kinds of methods in the `` protocol, each with a variation for working with paths, as well as methods for error handling:
-
-- `-fileManager:shouldMoveItemAtURL:toURL:`
-- `-fileManager:shouldCopyItemAtURL:toURL:`
-- `-fileManager:shouldRemoveItemAtURL:`
-- `-fileManager:shouldLinkItemAtURL:toURL:`
-
-If you were wondering when you might `alloc init` your own `NSFileManager` rather than using the shared instance, this is it. As per the documentation:
-
-> If you use a delegate to receive notifications about the status of move, copy, remove, and link operations, you should create a unique instance of the file manager object, assign your delegate to that object, and use that file manager to initiate your operations.
-
-~~~{objective-c}
-NSFileManager *fileManager = [[NSFileManager alloc] init];
-fileManager.delegate = delegate;
-
-NSURL *bundleURL = [[NSBundle mainBundle] bundleURL];
-NSArray *contents = [fileManager contentsOfDirectoryAtURL:bundleURL
- includingPropertiesForKeys:@[]
- options:NSDirectoryEnumerationSkipsHiddenFiles
- error:nil];
-
-for (NSString *filePath in contents) {
- [fileManager removeItemAtPath:filePath error:nil];
-}
-~~~
-
-#### CustomFileManagerDelegate.m
-
-~~~{objective-c}
-#pragma mark - NSFileManagerDelegate
-
-- (BOOL)fileManager:(NSFileManager *)fileManager
-shouldRemoveItemAtURL:(NSURL *)URL
-{
- return ![[[URL lastPathComponent] pathExtension] isEqualToString:@"pdf"];
-}
-~~~
-
-## Ubiquitous Storage
-
-Documents can also be moved to iCloud. If you guessed that this would be anything but straight forward, you'd be 100% correct.
-
-This is another occasion when you'd `alloc init` your own `NSFileManager` rather than using the shared instance. Because `URLForUbiquityContainerIdentifier:` and `setUbiquitous:itemAtURL:destinationURL:error:` are blocking calls, this entire operation needs to be dispatched to a background queue.
-
-### Moving an Item to Ubiquitous Storage
-
-~~~{objective-c}
-dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
- NSFileManager *fileManager = [[NSFileManager alloc] init];
- NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
- NSURL *fileURL = [NSURL fileURLWithPath:[documentsPath stringByAppendingPathComponent:@"Document.pages"]];
-
- // Defaults to first listed in entitlements when `nil`; should replace with real identifier
- NSString *identifier = nil;
-
- NSURL *ubiquitousContainerURL = [fileManager URLForUbiquityContainerIdentifier:identifier];
- NSURL *ubiquitousFileURL = [ubiquitousContainerURL URLByAppendingPathComponent:@"Document.pages"];
-
- NSError *error = nil;
- BOOL success = [fileManager setUbiquitous:YES
- itemAtURL:fileURL
- destinationURL:ubiquitousFileURL
- error:&error];
- if (!success) {
- NSLog(@"[Error] %@ (%@) (%@)", error, fileURL, ubiquitousFileURL);
- }
-});
-~~~
-
-> You can find more information about ubiquitous document storage in Apple's "iCloud File Management" document.
-
-* * *
-
-There's a lot to know about file systems, but as an app developer, it's mostly an academic exercise. Now don't get me wrong—academic exercises are great! But they don't ship code. `NSFileManager` allows you to ignore most of the subtlety of all of this and get things done.
diff --git a/2013-12-02-nsnotification-and-nsnotificationcenter.md b/2013-12-02-nsnotification-and-nsnotificationcenter.md
index baceb91a..779baa9d 100644
--- a/2013-12-02-nsnotification-and-nsnotificationcenter.md
+++ b/2013-12-02-nsnotification-and-nsnotificationcenter.md
@@ -1,6 +1,6 @@
---
title: "NSNotification & NSNotificationCenter"
-author: Mattt Thompson
+author: Mattt
category: Cocoa
tags: popular
excerpt: "Any idea is inextricably linked to how its communicated. A medium defines the form and scale of significance in such a way to shape the very meaning of an idea. Very truly, the medium is the message."
@@ -39,7 +39,7 @@ This is as true of humans as it is within a computer process. In Cocoa, there ar
-
Notifications
+
Notifications
@@ -59,7 +59,7 @@ This is as true of humans as it is within a computer process. In Cocoa, there ar
-We've discussed the importance of how events are communicated in APIs previously in our [article on Key-Value Observing](http://nshipster.com/key-value-observing/). This week, we'll expand our look at the available options, with `NSNotificationCenter` & `NSNotification`.
+We've discussed the importance of how events are communicated in APIs previously in our [article on Key-Value Observing](https://nshipster.com/key-value-observing/). This week, we'll expand our look at the available options, with `NSNotificationCenter` & `NSNotification`.
* * *
@@ -85,13 +85,13 @@ The `name` and `object` parameters of both methods are used to decide whether th
> *See for yourself! An ordinary iOS app fires dozens of notifications just in the first second of being launched—many that you've probably never heard of before, nor will ever have to think about again.
-~~~{swift}
+```swift
let center = NSNotificationCenter.defaultCenter()
center.addObserverForName(nil, object: nil, queue: nil) { notification in
print("\(notification.name): \(notification.userInfo ?? [:])")
}
-~~~
-~~~{objective-c}
+```
+```objc
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserverForName:nil
object:nil
@@ -100,7 +100,7 @@ NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
{
NSLog(@"%@", notification.name);
}];
-~~~
+```
### Removing Observers
@@ -118,23 +118,23 @@ Notification names are generally defined as string constants. Like any string co
Keys for `userInfo` should likewise be defined as string constants. It's important to clearly document the expected kinds of values for each key, since the compiler can't enforce constraints on dictionaries the same way it can for an object.
-~~~{swift}
+```swift
class FooController : UIViewController {
enum Notifications {
static let FooDidBar = "XXFooDidBarNotification"
static let FooDidBazoom = "XXFooDidBazoomNotification"
}
- // ...
+ <#...#>
}
-~~~
-~~~{objective-c}
+```
+```objc
// Foo.h
extern NSString * const XXFooDidBarNotification;
// Foo.m
NSString * const XXFooDidBarNotification = @"XXFooDidBarNotification";
-~~~
+```
Notifications are posted with `–postNotificationName:object:userInfo:` or its convenience method `–postNotificationName:object:`, which passes `nil` for `userInfo`. `–postNotification:` is also available, but it's generally preferable to have the notification object creation handled by the method itself.
@@ -144,25 +144,25 @@ Since notification dispatch happens on the posting thread, it may be necessary t
## KVO != NSNotificationCenter
-Something that often slips up developers is how similar the method signatures for [Key-Value Observing](http://nshipster.com/key-value-observing/) are to those of `NSNotificationCenter`:
+Something that often slips up developers is how similar the method signatures for [Key-Value Observing](https://nshipster.com/key-value-observing/) are to those of `NSNotificationCenter`:
#### Key-Value Observing
-~~~{swift}
+```swift
func addObserver(observer: NSObject, forKeyPath keyPath: String,
options: NSKeyValueObservingOptions,
context: UnsafeMutablePointer)
-~~~
-~~~{objective-c}
+```
+```objc
- (void)addObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
context:(void *)context
-~~~
+```
#### NSNotificationCenter
-~~~{swift}
+```swift
func addObserver(observer: AnyObject,
selector aSelector: Selector,
name aName: String?,
@@ -172,8 +172,8 @@ func addObserverForName(name: String?,
object obj: AnyObject?,
queue: NSOperationQueue?,
usingBlock block: (NSNotification) -> Void) -> NSObjectProtocol
-~~~
-~~~{objective-c}
+```
+```objc
- (void)addObserver:(id)notificationObserver
selector:(SEL)notificationSelector
name:(NSString *)notificationName
@@ -183,7 +183,7 @@ func addObserverForName(name: String?,
object:(id)obj
queue:(NSOperationQueue *)queue
usingBlock:(void (^)(NSNotification *))block
-~~~
+```
**Key-Value Observing adds observers for keypaths, while NSNotificationCenter adds observers for notifications.** Keep this distinction clear in your mind, and proceed to use both APIs confidently.
diff --git a/2013-12-09-multipeer-connectivity.md b/2013-12-09-multipeer-connectivity.md
index 4aca63e3..0d6c5070 100644
--- a/2013-12-09-multipeer-connectivity.md
+++ b/2013-12-09-multipeer-connectivity.md
@@ -1,6 +1,6 @@
---
title: Multipeer Connectivity
-author: Mattt Thompson
+author: Mattt
category: Cocoa
tags: popular
excerpt: "As consumer web technologies and enterprises race towards cloud infrastructure, there is a curious and significant counter-movement towards connected devices. The Multipeer Connectivity APIs, introduced in iOS 7, therefore may well be the most significant for the platform."
@@ -26,15 +26,15 @@ Advertising makes a service known to other peers, while discovery is the inverse
Each service is identified by a type, which is a short text string of ASCII letters, numbers, and dashes, up to 15 characters in length. By convention, a service name should begin with the app name, followed by a dash and a unique descriptor for that service (think of it as simplified `com.apple.*`-esque reverse-DNS notation):
-~~~{objective-c}
+```objc
static NSString * const XXServiceType = @"xx-service";
-~~~
+```
Peers are uniquely identified by an `MCPeerID` object, which are initialized with a display name. This could be a user-specified nickname, or simply the current device name:
-~~~{objective-c}
+```objc
MCPeerID *localPeerID = [[MCPeerID alloc] initWithDisplayName:[[UIDevice currentDevice] name]];
-~~~
+```
> Peers can be also be advertised or discovered manually using `NSNetService` or the Bonjour C APIs, but this is a rather advanced and specific concern. Additional information about manual peer management can be found in the `MCSession` documentation.
@@ -44,20 +44,20 @@ Services are advertised by the `MCNearbyServiceAdvertiser`, which is initialized
> Discovery information is sent as Bonjour `TXT` records encoded according to [RFC 6763](http://tools.ietf.org/html/rfc6763).
-~~~{objective-c}
+```objc
MCNearbyServiceAdvertiser *advertiser =
[[MCNearbyServiceAdvertiser alloc] initWithPeer:localPeerID
discoveryInfo:nil
serviceType:XXServiceType];
advertiser.delegate = self;
[advertiser startAdvertisingPeer];
-~~~
+```
Events are handled by the advertiser's `delegate`, conforming to the `MCNearbyServiceAdvertiserDelegate` protocol.
As an example implementation, consider a client that allows the user to choose whether to accept or reject incoming connection requests, with the option to reject and block any subsequent requests from that peer:
-~~~{objective-c}
+```objc
#pragma mark - MCNearbyServiceAdvertiserDelegate
- (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser
@@ -90,7 +90,7 @@ didReceiveInvitationFromPeer:(MCPeerID *)peerID
invitationHandler(acceptedInvitation, (acceptedInvitation ? session : nil));
}] showInView:self.view];
}
-~~~
+```
> For sake of simplicity, this example contrives a block-based initializer for `UIActionSheet`, which allows for the `invitationHandler` to be passed directly into the action sheet responder in order to avoid the messy business of creating and managing a custom delegate object. This method can be implemented in a category, or adapted from [any of the implementations available on CocoaPods](http://cocoapods.org/?q=uiactionsheet%20blocks)
@@ -98,12 +98,12 @@ didReceiveInvitationFromPeer:(MCPeerID *)peerID
As in the example above, sessions are created by advertisers, and passed to peers when accepting an invitation to connect. An `MCSession` object is initialized with the local peer identifier, as well as `securityIdentity` and `encryptionPreference` parameters.
-~~~{objective-c}
+```objc
MCSession *session = [[MCSession alloc] initWithPeer:localPeerID
securityIdentity:nil
encryptionPreference:MCEncryptionNone];
session.delegate = self;
-~~~
+```
`securityIdentity` is an optional parameter that allows peers to securely identify peers by X.509 certificates. When specified, the first object should be an `SecIdentityRef` identifying the client, followed by one or more `SecCertificateRef` objects than can be used to verify the local peer’s identity.
@@ -121,14 +121,14 @@ The `MCSessionDelegate` protocol will be covered in the section on sending and r
Clients can discover advertised services using `MCNearbyServiceBrowser`, which is initialized with the local peer identifier and the service type, much like for `MCNearbyServiceAdvertiser`.
-~~~{objective-c}
+```objc
MCNearbyServiceBrowser *browser = [[MCNearbyServiceBrowser alloc] initWithPeer:localPeerID serviceType:XXServiceType];
browser.delegate = self;
-~~~
+```
There may be many peers advertising a particular service, so as a convenience to the user (and the developer), the `MCBrowserViewController` offers a built-in, standard way to present and connect to advertising peers:
-~~~{objective-c}
+```objc
MCBrowserViewController *browserViewController =
[[MCBrowserViewController alloc] initWithBrowser:browser
session:session];
@@ -139,7 +139,7 @@ browserViewController.delegate = self;
^{
[browser startBrowsingForPeers];
}];
-~~~
+```
When a browser has finished connecting to peers, it calls `-browserViewControllerDidFinish:` on its delegate, to notify the presenting view controller that it should update its UI to accommodate the newly-connected clients.
@@ -155,7 +155,7 @@ Once peers are connected to one another, information can be sent between them. T
Messages are sent with `-sendData:toPeers:withMode:error:`:
-~~~{objective-c}
+```objc
NSString *message = @"Hello, World!";
NSData *data = [message dataUsingEncoding:NSUTF8StringEncoding];
NSError *error = nil;
@@ -165,13 +165,13 @@ if (![self.session sendData:data
error:&error]) {
NSLog(@"[Error] %@", error);
}
-~~~
+```
* * *
Messages are received through the `MCSessionDelegate` method `-sessionDidReceiveData:fromPeer:`. Here's how one would decode the message sent in the previous code example:
-~~~{objective-c}
+```objc
#pragma mark - MCSessionDelegate
- (void)session:(MCSession *)session
@@ -183,12 +183,12 @@ Messages are received through the `MCSessionDelegate` method `-sessionDidReceive
encoding:NSUTF8StringEncoding];
NSLog(@"%@", message);
}
-~~~
+```
Another approach would be to send `NSKeyedArchiver`-encoded objects:
-~~~{objective-c}
-id object = // ...;
+```objc
+id object = <#...#>;
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:object];
NSError *error = nil;
if (![self.session sendData:data
@@ -197,9 +197,9 @@ if (![self.session sendData:data
error:&error]) {
NSLog(@"[Error] %@", error);
}
-~~~
+```
-~~~{objective-c}
+```objc
#pragma mark - MCSessionDelegate
- (void)session:(MCSession *)session
@@ -212,15 +212,15 @@ if (![self.session sendData:data
[unarchiver finishDecoding];
NSLog(@"%@", object);
}
-~~~
+```
-> In order to guard against object substitution attacks, it is important to set `requiresSecureCoding` to `YES`, such that an exception is thrown if the root object class does not conform to ``. For more information, see the [NSHipster article on [NSSecureCoding](http://nshipster.com/nssecurecoding/).
+> In order to guard against object substitution attacks, it is important to set `requiresSecureCoding` to `YES`, such that an exception is thrown if the root object class does not conform to ``. For more information, see the [NSHipster article on [NSSecureCoding](https://nshipster.com/nssecurecoding/).
### Streams
Streams are created with `-startStreamWithName:toPeer:`:
-~~~{objective-c}
+```objc
NSOutputStream *outputStream =
[session startStreamWithName:name
toPeer:peer];
@@ -230,14 +230,14 @@ stream.delegate = self;
forMode:NSDefaultRunLoopMode];
[stream open];
-// ...
-~~~
+<#...#>
+```
* * *
Streams are received by the `MCSessionDelegate` with `-session:didReceiveStream:withName:fromPeer:`:
-~~~{objective-c}
+```objc
#pragma mark - MCSessionDelegate
- (void)session:(MCSession *)session
@@ -250,7 +250,7 @@ didReceiveStream:(NSInputStream *)stream
forMode:NSDefaultRunLoopMode];
[stream open];
}
-~~~
+```
Both the input and output streams must be scheduled and opened before they can be used. Once that's done, streams can be read from and written to just like any other bound pair.
@@ -258,7 +258,7 @@ Both the input and output streams must be scheduled and opened before they can b
Resources are sent with `sendResourceAtURL:withName:toPeer:withCompletionHandler:`:
-~~~{objective-c}
+```objc
NSURL *fileURL = [NSURL fileURLWithPath:@"path/to/resource"];
NSProgress *progress =
[self.session sendResourceAtURL:fileURL
@@ -268,15 +268,15 @@ NSProgress *progress =
{
NSLog(@"[Error] %@", error);
}];
-~~~
+```
-The returned `NSProgress` object can be [Key-Value Observed](http://nshipster.com/key-value-observing/) to monitor progress of the file transfer, as well as provide a cancellation handler, through the `-cancel` method.
+The returned `NSProgress` object can be [Key-Value Observed](https://nshipster.com/key-value-observing/) to monitor progress of the file transfer, as well as provide a cancellation handler, through the `-cancel` method.
* * *
Receiving resources happens across two methods in `MCSessionDelegate`: `-session:didStartReceivingResourceWithName:fromPeer:withProgress:` & `-session:didFinishReceivingResourceWithName:fromPeer:atURL:withError:`:
-~~~{objective-c}
+```objc
#pragma mark - MCSessionDelegate
- (void)session:(MCSession *)session
@@ -284,7 +284,7 @@ didStartReceivingResourceWithName:(NSString *)resourceName
fromPeer:(MCPeerID *)peerID
withProgress:(NSProgress *)progress
{
- // ...
+ <#...#>
}
- (void)session:(MCSession *)session
@@ -301,7 +301,7 @@ didFinishReceivingResourceWithName:(NSString *)resourceName
NSLog(@"[Error] %@", error);
}
}
-~~~
+```
Again, the `NSProgress` parameter in `-session:didStartReceivingResourceWithName:fromPeer:withProgress:` allows the receiving peer to monitor the file transfer progress. In `-session:didFinishReceivingResourceWithName:fromPeer:atURL:withError:`, it is the responsibility of the delegate to move the file at the temporary `localURL` to a permanent location.
diff --git a/2013-12-16-launch-options.md b/2013-12-16-launch-options.md
index 549f2b42..50347249 100644
--- a/2013-12-16-launch-options.md
+++ b/2013-12-16-launch-options.md
@@ -1,6 +1,6 @@
---
title: UIApplicationDelegate launchOptions
-author: Mattt Thompson
+author: Mattt
category: Cocoa
excerpt: "AppDelegate is the dumping ground for functionality in iOS."
status:
@@ -21,7 +21,7 @@ This week, all will be revealed in this NSHipster tell-all about the least under
Every app begins with `UIApplicationDelegate -application:didFinishLaunchingWithOptions:` (or more accurately, `-application:willFinishLaunchingWithOptions:`, when implemented). It is called by the application to notify its delegate that the launch process is finishing, and nearly ready to run.
-An app launches when its icon is tapped on [Springboard](http://en.wikipedia.org/wiki/SpringBoard), but there are several other occasions in which an app can be launched. For example, an app registered for a custom URL scheme, such as `twitter://`, could be launched as a result of opening a URL. An app could also be launched in response to a push notification, or a significant change in device location.
+An app launches when its icon is tapped on [Springboard](https://en.wikipedia.org/wiki/SpringBoard), but there are several other occasions in which an app can be launched. For example, an app registered for a custom URL scheme, such as `twitter://`, could be launched as a result of opening a URL. An app could also be launched in response to a push notification, or a significant change in device location.
Determining why and how an app launched is the responsibility of the `launchOptions` parameter. Like a `userInfo` dictionary, `-application:didFinishLaunchingWithOptions:` can get information for particular named keys in `launchOptions`.
@@ -33,9 +33,9 @@ Numerous as they are, `launchOptions` keys can be more easily understood when or
Apps can launch other apps by passing URLs:
-~~~{objective-c}
-[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"app://..."]];
-~~~
+```objc
+[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"app:<#...#>"]];
+```
For example, an `http://` URL would open in Safari, a `mailto://` URL would open in Mail, and a `tel://` URL would open in Phone.
@@ -48,7 +48,7 @@ An app can also be launched through URLs with additional system information. Whe
> - `UIApplicationLaunchOptionsSourceApplicationKey`: Identifies the app that requested the launch of your app. The value of this key is an `NSString` object that represents the bundle ID of the app that made the request
> - `UIApplicationLaunchOptionsAnnotationKey`: Indicates that custom data was provided by the app that requested the opening of the URL. The value of this key is a property-list object containing the custom data.
-~~~{objective-c}
+```objc
NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"Document" withExtension:@"pdf"];
if (fileURL) {
UIDocumentInteractionController *documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:fileURL];
@@ -56,11 +56,11 @@ if (fileURL) {
[documentInteractionController setDelegate:self];
[documentInteractionController presentPreviewAnimated:YES];
}
-~~~
+```
## Responding to Notification
-Not to be confused with [`NSNotification`](http://nshipster.com/nsnotification-and-nsnotificationcenter/), apps can be sent remote or local notifications.
+Not to be confused with [`NSNotification`](https://nshipster.com/nsnotification-and-nsnotificationcenter/), apps can be sent remote or local notifications.
### Remote Notification
@@ -68,12 +68,12 @@ Introduced in iOS 3, remote, or "push" notifications are one of the defining fea
To register for remote notifications, `registerForRemoteNotificationTypes:` is called in `application:didFinishLaunchingWithOptions:`.
-~~~{objective-c}
+```objc
[application registerForRemoteNotificationTypes:
- UIRemoteNotificationTypeBadge |
+ UIRemoteNotificationTypeBadge |
UIRemoteNotificationTypeSound |
- UIRemoteNotificationTypeAlert];
-~~~
+ UIRemoteNotificationTypeAlert];
+```
...which, if successful, calls `-application:didRegisterForRemoteNotificationsWithDeviceToken:`. Once the device has been successfully registered, it can receive push notifications at any time.
@@ -86,17 +86,17 @@ If an app receives a notification while in the foreground, its delegate will cal
Since this introduces two separate code paths for notification handling, a common approach is to have `application:didFinishLaunchingWithOptions:` manually call `application:didReceiveRemoteNotification:`:
-~~~{objective-c}
+```objc
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
- // ...
+ <#...#>
if (launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]) {
[self application:application didReceiveRemoteNotification:launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]];
}
}
-~~~
+```
### Local Notification
@@ -112,15 +112,15 @@ A local notification populates the launch options on `UIApplicationLaunchOptions
In the case where it is desirable to show an alert for a local notification delivered when the app is active in the foreground, and otherwise wouldn't provide a visual indication, here's how one might use the information from `UILocalNotification` to do it manually:
-~~~{objective-c}
-// .h
+```objc
+// AppDelegate.h
@import AVFoundation;
@interface AppDelegate ()
@property (readwrite, nonatomic, assign) SystemSoundID localNotificationSound;
@end
-// .m
+// AppDelegate.m
- (void)application:(UIApplication *)application
didReceiveLocalNotification:(UILocalNotification *)notification
{
@@ -148,7 +148,7 @@ didReceiveLocalNotification:(UILocalNotification *)notification
AudioServicesDisposeSystemSoundID(self.localNotificationSound);
}
}
-~~~
+```
## Location Event
@@ -160,7 +160,7 @@ But fear not! With iOS region monitoring, your app can be launched on location e
Here's an example of how an app might go about monitoring for significant location change to determine launch behavior:
-~~~{objective-c}
+```objc
// .h
@import CoreLocation;
@@ -172,7 +172,7 @@ Here's an example of how an app might go about monitoring for significant locati
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
- // ...
+ <#...#>
if (![CLLocationManager locationServicesEnabled]) {
[[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Location Services Disabled", nil)
@@ -190,7 +190,7 @@ didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
[self.locationManager startUpdatingLocation];
}
}
-~~~
+```
## Newsstand
@@ -204,10 +204,10 @@ Newsstand can launch when newly-downloaded assets are available.
This is how you register:
-~~~{objective-c}
+```objc
[application registerForRemoteNotificationTypes:
- UIRemoteNotificationTypeNewsstandContentAvailability];
-~~~
+ UIRemoteNotificationTypeNewsstandContentAvailability];
+```
And this is the key to look out for in `launchOptions`:
@@ -224,7 +224,7 @@ If an app launches, instantiates a `CBCentralManager` or `CBPeripheralManager` w
> - `UIApplicationLaunchOptionsBluetoothCentralsKey`: Indicates that the app previously had one or more `CBCentralManager` objects and was relaunched by the Bluetooth system to continue actions associated with those objects. The value of this key is an `NSArray` object containing one or more `NSString` objects. Each string in the array represents the restoration identifier for a central manager object.
> - `UIApplicationLaunchOptionsBluetoothPeripheralsKey`: Indicates that the app previously had one or more `CBPeripheralManager` objects and was relaunched by the Bluetooth system to continue actions associated with those objects. The value of this key is an `NSArray` object containing one or more `NSString` objects. Each string in the array represents the restoration identifier for a peripheral manager object.
-~~~{objective-c}
+```objc
// .h
@import CoreBluetooth;
@@ -241,7 +241,7 @@ if (self.centralManager.state == CBCentralManagerStatePoweredOn) {
NSDictionary *scanOptions = @{CBCentralManagerScanOptionAllowDuplicatesKey:@YES};
[self.centralManager scanForPeripheralsWithServices:services options:scanOptions];
}
-~~~
+```
* * *
diff --git a/2014-01-01-new-years-2014.md b/2014-01-01-new-years-2014.md
index ed6413c2..af262f4a 100644
--- a/2014-01-01-new-years-2014.md
+++ b/2014-01-01-new-years-2014.md
@@ -1,27 +1,27 @@
---
title: "Reader Submissions - New Year's 2014"
-author: Mattt Thompson
+author: Mattt
category: Reader Submissions
excerpt: "As we prepare to increment our NSDateComponents -year by 1, it's time once again for NSHipster end-of-the-year Reader Submissions!"
status:
- swift: n/a
+ swift: n/a
---
-As we prepare to increment our `NSDateComponents -year` by `1`, it's time once again for NSHipster end-of-the-year Reader Submissions! [Last year](https://gist.github.com/mattt/4148342), we got some [mind-blowing tips and tricks](http://nshipster.com/reader-submissions-new-years-2013/). With the release of iOS 7 & Mavericks, and a year's worth of new developments in the Objective-C ecosystem, there was a ton of new stuff to write about.
+As we prepare to increment our `NSDateComponents -year` by `1`, it's time once again for NSHipster end-of-the-year Reader Submissions! [Last year](https://gist.github.com/mattt/4148342), we got some [mind-blowing tips and tricks](https://nshipster.com/reader-submissions-new-years-2013/). With the release of iOS 7 & Mavericks, and a year's worth of new developments in the Objective-C ecosystem, there was a ton of new stuff to write about.
Thanks to [Arnaud Coomans](https://github.com/acoomans), [Cédric Luthi](https://github.com/0xced), [David Grandinetti](https://github.com/dbgrandi), [Ell Neal](https://github.com/ellneal), [Eric Allam](https://github.com/rubymaverick), [Erik Kerber](https://github.com/eskerber), [Jim Kubicek](https://github.com/jkubicek), [Joachim Bengtsson](https://github.com/nevyn), [Johannes Lund](https://github.com/Anviking), [Josh Avant](https://github.com/joshavant), [João Prado Maia](https://github.com/jpm), [Justin R. Miller](https://github.com/incanus), [Kamil Pyć](https://github.com/PycKamil), [Matthew Teece](https://github.com/mteece), [Maximilian Tagher](https://github.com/MaxGabriel), [Nigel Timothy Barber](https://github.com/mindbrix), [Nolan O'Brien](https://github.com/NSProgrammer), [Pitiphong Phongpattranont](https://github.com/pitiphong-p), [Steve Moser](https://gist.github.com/stevemoser), [Thomas Visser](https://github.com/Thomvis), [Vadim Shpakovski](https://github.com/shpakovski), & [@jurre](https://github.com/jurre) for [contributing their great tips](https://gist.github.com/7414618).
-* * *
+---
## GCC Code Block Evaluation C Extension
Let's make this official: **NSHipster's Objective-C trend of 2013 is code block evaluation assignment**. Recommended by both [Jim Kubicek](https://github.com/jkubicek) and [Maximilian Tagher](https://github.com/MaxGabriel) (citing [this blog post](http://cocoa-dom.tumblr.com/post/56517731293/new-thing-i-do-in-code) by [Dominik Wagner](https://github.com/monkeydom)), this trick does wonders to make code cleaner, safer, and more concise.
-Behind the magic is a GCC C extension, which causes a code block to return a value if enclosed within brackets and parentheses.
+Behind the magic is a GCC C extension, which causes a code block to return a value if enclosed within brackets and parentheses.
Watch, as it cuts through this view controller code like butter!
-~~~{objective-c}
+```objc
self.searchBar = ({
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:({
CGRect frame = self.tableView.frame;
@@ -31,7 +31,7 @@ self.searchBar = ({
searchBar.delegate = self;
searchBar;
});
-~~~
+```
This not only segregates configuration details into initialization, but the additional scope allows generic variable names like `frame`, `button`, and `view` to be reused in subsequent initializations. No more `loginButtonFrame = ... / signupButtonFrame = ...`!
@@ -39,42 +39,41 @@ If code craftsmanship is important to you, strongly consider making this standar
## Default Values with GNU-style Ternary `?:`
-
The ternary operator, `?`, is shorthand for `if () {...} else {...}`. However, because of how difficult it can be to understand statements with ternary operators at a glance, they are generally dispreferred by veteran coders.
Nonetheless, [Maximilian Tagher](https://github.com/MaxGabriel) offers a lesser-known (yet much-loved by those in-the-know) use of the ternary operator: `?:`, which acts as a convenient way to specify a fallback value to return if the left-hand side is `nil`.
-~~~{objective-c}
+```objc
NSLog(@"%@", @"a" ?: @"b"); // @"a"
NSLog(@"%@", nil ?: @"b"); // @"b"
-~~~
+```
This is especially convenient for providing default behavior when a property may not be set:
-~~~{objective-c}
+```objc
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
- // ...
+ <#...#>
});
-~~~
+```
The main downside of this approach is that default Xcode project warning settings will raise a warning. You can get around this by wrapping the relevant code block in `#pragma` declarations, but the added LOC nullifies much of the brevity that this approach provides:
-~~~{objective-c}
+```objc
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
self.name = name ?: @"Unknown";
#pragma clang diagnostic pop
-~~~
+```
## `@import`
[Vadim Shpakovski](https://github.com/shpakovski) reminds us of one of the more obscure additions to LLVM 5.0, the `@import` keyword. No more are the days of `Xcode ▹ Project ▹ TARGETS ▹ General ▹ Linked Frameworks and Libraries ▹ +`. With `@import`, the Xcode will automatically link `MapKit`, `CoreData`, or any other referenced framework as necessary. Even `Prefix.pch` benefits, sporting a svelte new physique:
-~~~{objective-c}
+```objc
@import UIKit;
@import Foundation;
@import CoreGraphics;
-~~~
+```
## Customizing MKMapView Tiles
@@ -90,7 +89,6 @@ Speaking of iOS maps, [João Prado Maia](https://github.com/jpm) cites an amazin
Shifting gears a little bit, [Eric Allam](https://github.com/rubymaverick) remarks that `NSAttributedString` can do HTML now in iOS 7 with the new [`NSHTMLTextDocumentType`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/NSAttributedString_UIKit_Additions/Reference/Reference.html#//apple_ref/doc/uid/TP40011688) document type attribute. Combine with [MMMarkdown](https://github.com/mdiep/MMMarkdown) for ridiculously easy [Markdown rendering in a `UITextView`](http://initwithfunk.com/blog/2013/09/29/easy-markdown-rendering-with-nsattributedstring-on-ios-7/).
-
---
## Launch Arguments & User Defaults
@@ -113,14 +111,14 @@ The command line argument `-TestFeatureEnabled YES` can be checked in code with
[David Grandinetti](https://github.com/dbgrandi) has a tip for apps that want to track outgoing links from within an app: override `AppDelegate -openURL:`:
-~~~{objective-c}
+```objc
-(BOOL)openURL:(NSURL *)url{
if ([[url scheme] hasPrefix:@"http"]) {
[[GAI sharedInstance].defaultTracker sendView:[url absoluteString]];
}
return [super openURL:url];
}
-~~~
+```
In this example, this information is being sent to Google Analytics, but one could easily adapt this approach for any analytics provider.
@@ -128,9 +126,9 @@ In this example, this information is being sent to Google Analytics, but one cou
Still resisting the aesthetics of this year's iOS makeover? [Kamil Pyć](https://github.com/PycKamil) shows us how to act as if iOS 7 never happened:
-~~~{objective-c}
+```objc
[[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"UIUseLegacyUI"]
-~~~
+```
> And, of course, following up from a previous tip, since `NSUserDefaults` is tied to launch arguments, this can also be specified on launch. Keep this tucked in the back of your mind—this could make for a simple yet effective April Fool's joke.
@@ -148,7 +146,7 @@ Fortunately, [Cédric Luthi](https://github.com/0xced) shows us how to tell if a
## A .plist of Emoji, Grouped by Category
-In an encore submission, Cédric brings us a [.plist file of Emoji grouped by category](https://gist.github.com/mattt/8185075) (mirrored from CloudApp to Gist in order to be more searchable). 😄👍
+In an encore submission, Cédric brings us a [.plist file of Emoji grouped by category](https://gist.github.com/mattt/8185075) (mirrored from CloudApp to Gist in order to be more searchable). 😄👍
---
@@ -156,7 +154,7 @@ In an encore submission, Cédric brings us a [.plist file of Emoji grouped by c
Here's a simple function from [Nolan O'Brien](https://github.com/NSProgrammer) that can be used to determine the type of image data based on the first couple bytes of the header:
-~~~{objective-c}
+```objc
static inline NSPUIImageType NSPUIImageTypeFromData(NSData *imageData) {
if (imageData.length > 4) {
const unsigned char * bytes = [imageData bytes];
@@ -179,38 +177,38 @@ static inline NSPUIImageType NSPUIImageTypeFromData(NSData *imageData) {
return NSPUIImageType_Unknown;
}
-~~~
+```
---
## Print KVO Context
-Once again, showing off his unmatched knowledge of Objective-C internals, Cédric shares this extremely useful tip for debugging [Key-Value Observing](http://nshipster.com/key-value-observing/).
+Once again, showing off his unmatched knowledge of Objective-C internals, Cédric shares this extremely useful tip for debugging [Key-Value Observing](https://nshipster.com/key-value-observing/).
> Print which [context](https://gist.github.com/ddribin/5158614#comment-798104) is passed to `observeValueForKeyPath:ofObject:change:context:` in lldb.
->Say you have declared a context like this:
+> Say you have declared a context like this:
-~~~{objective-c}
+```objc
static const void *MyFooContext = &MyFooContext;
-~~~
+```
> ...and you want to to know what context it is when you are inside
-~~~{objective-c}
+```objc
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
-~~~
+```
> You can do this:
-~~~
+```
(lldb) image lookup -a `context`
Address: MyApp[0x00026258] (MyApp.__DATA.__data + 4)
Summary: MyFooContext
-~~~
+```
---
@@ -218,7 +216,7 @@ static const void *MyFooContext = &MyFooContext;
On the subject of Key-Value Coding, [Pitiphong Phongpattranont](https://github.com/pitiphong-p) offers this useful function that builds a keypath from a variable list of selectors:
-~~~{objective-c}
+```objc
inline NSString * PTPKeyPathForSelectors(SEL selector, ...) {
if (!selector) {
return nil;
@@ -235,22 +233,22 @@ inline NSString * PTPKeyPathForSelectors(SEL selector, ...) {
return [selectors componentsJoinedByString:@"."];
}
-~~~
+```
-~~~{objective-c}
+```objc
NSString *keyPath = PTPKeyPathForSelectors(@selector(data), @selector(name), nil);
// => @"data.name"
-~~~
+```
## Nomad CLI Utilities
And finally, [Matthew Teece](https://github.com/mteece) gives a shout-out to [Nomad](http://nomad-cli.com), a world-class collection of command-line utilities—specifically, [Houston](https://github.com/nomad/houston), which can send and manage push notifications from the command line, or within your Ruby application.
-~~~{bash}
+```terminal
$ apn push "" -c /path/to/cert.pem -m "Hello!"
-~~~
+```
-* * *
+---
Thus concludes this year's reader submissions. Thanks again to everyone for your submissions!
diff --git a/2014-01-13-nsrange.md b/2014-01-13-nsrange.md
index 1af331bc..8ec3a231 100644
--- a/2014-01-13-nsrange.md
+++ b/2014-01-13-nsrange.md
@@ -1,44 +1,44 @@
---
title: NSRange
-author: Mattt Thompson
+author: Mattt
category: Cocoa
excerpt: "NSRange is one of the essential types of Foundation. Passed around and returned in methods throughout the framework, being well-versed in this struct has a range of benefits."
status:
- swift: n/a
+ swift: n/a
---
`NSRange` is one of the essential types of Foundation. Passed around and returned in methods throughout the framework, being well-versed in this struct has a range of benefits, which this week's article will help you locate.
-* * *
+---
Ranges are data types used to describe a contiguous interval of integers. They are most commonly used with strings, arrays, and similarly-ordered collections.
For Objective-C programs, the Foundation type `NSRange` is used. In other languages, ranges are often encoded as a two-element array, containing the start and end indexes. In Foundation, `NSRange` instead encodes a range as struct containing the location and length. By command-clicking (`⌘-ʘ`) on the `NSRange` symbol in Xcode, we can jump directly to its declaration in `Foundation/NSRange.h`:
-~~~{objective-c}
+```objc
typedef struct _NSRange {
NSUInteger location;
NSUInteger length;
} NSRange;
-~~~
+```
In practice, this approach helps mitigate common off-by-one errors when working with ranges. For example, compare the equivalent Javascript and Objective-C code for creating a range of characters for a given string:
#### range.js
-~~~{javascript}
+```javascript
var string = "hello, world";
var range = [0, string.length - 1];
-~~~
+```
Forgetting to subtract `1` for the end index in Javascript would result in an out-of-bounds error later.
#### range.m
-~~~{objective-c}
+```objc
NSString *string = @"hello, world";
NSRange range = NSMakeRange(0, [string length]);
-~~~
+```
`NSRange`'s approach is clearer and less prone to error—especially when it comes to more complex arithmetic operations on ranges.
@@ -46,142 +46,142 @@ NSRange range = NSMakeRange(0, [string length]);
### Strings
-~~~{objective-c}
+```objc
NSString *string = @"lorem ipsum dolor sit amet";
NSRange range = [string rangeOfString:@"ipsum"];
// {.location=6, .length=5}
NSString *substring = [string substringWithRange:range];
// @"ipsum"
-~~~
+```
`NSString` does not have a method like `containsString:`. Instead, `rangeOfString:` can be used to check for an `NSNotFound` location value:
-~~~{objective-c}
+```objc
NSString *input = ...;
if ([input rangeOfString:@"keyword"].location != NSNotFound) {
- // ...
+ <#...#>
}
-~~~
+```
### Arrays
-~~~{objective-c}
+```objc
NSArray *array = @[@"a", @"b", @"c", @"d"];
NSArray *subarray = [array subarrayWithRange:NSMakeRange(1, 2)];
// @[@"b", @"c"]
-~~~
+```
### Index Sets
-[NSIndexSet](http://nshipster.com/nsindexset/) is a Foundation collection class that is similar to `NSRange`, with the notable exception of being able to support non-contiguous series. An `NSIndexSet` can be created from a range using the `indexSetWithIndexesInRange:` class constructor:
+[NSIndexSet](https://nshipster.com/nsindexset/) is a Foundation collection class that is similar to `NSRange`, with the notable exception of being able to support non-contiguous series. An `NSIndexSet` can be created from a range using the `indexSetWithIndexesInRange:` class constructor:
-~~~{objective-c}
+```objc
NSRange range = NSMakeRange(0, 10);
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:range];
-~~~
+```
## Functions
Because `NSRange` is not a class, creating and using instances is done through function calls, rather than, say, instance methods.
-> Many of the NSRange functions are named counter to the modern conventions of Foundation and CoreFoundation wherein the relevant type of the function immediately follows the two-letter namespace. For example, `NSMakeRange` should instead be named `NSRangeMake`, following the example of `CGRectMake` and `CGSizeMake`, et al. Similarly, a better name for `NSEqualRanges` would be `NSRangeEqualToRange`, just like `CGPointEqualToPoint`.
+> Many of the NSRange functions are named counter to the modern conventions of Foundation and CoreFoundation wherein the relevant type of the function immediately follows the two-letter namespace. For example, `NSMakeRange` should instead be named `NSRangeMake`, following the example of `CGRectMake` and `CGSizeMake`, et al. Similarly, a better name for `NSEqualRanges` would be `NSRangeEqualToRange`, just like `CGPointEqualToPoint`.
>
> Although consistency in itself is likely not sufficient reason to go through the trouble of replacing existing usage, [this gist](https://gist.github.com/mattt/8402537) shows how one could make their own code base a little more OCD-friendly.
### Creating an NSRange
-> - `NSMakeRange`: Creates a new NSRange from the specified values.
+> - `NSMakeRange`: Creates a new NSRange from the specified values.
-~~~{objective-c}
+```objc
NSArray *array = @[@1, @2, @3];
NSRange range = NSMakeRange(0, [array count]);
// {.location=0, .length=3}
-~~~
+```
### Querying Information
> - `NSEqualRanges`: Returns a Boolean value that indicates whether two given ranges are equal.
-~~~{objective-c}
+```objc
NSRange range1 = NSMakeRange(0, 6);
NSRange range2 = NSMakeRange(2, 7);
BOOL equal = NSEqualRanges(range1, range2); // NO
-~~~
+```
-> - `NSLocationInRange`: Returns a Boolean value that indicates whether a specified position is in a given range.
+> - `NSLocationInRange`: Returns a Boolean value that indicates whether a specified position is in a given range.
-~~~{objective-c}
+```objc
NSRange range = NSMakeRange(3, 4);
BOOL contained = NSLocationInRange(5, range); // YES
-~~~
+```
> - `NSMaxRange`: Returns the sum of the location and length of the range.
-~~~{objective-c}
+```objc
NSRange range = NSMakeRange(3, 4);
NSUInteger max = NSMaxRange(range); // 7
-~~~
+```
### Set Operations
> - `NSIntersectionRange`: Returns the intersection of the specified ranges. If the returned range’s length field is `0`, then the two ranges don’t intersect, and the value of the location field is undefined.
-~~~{objective-c}
+```objc
NSRange range1 = NSMakeRange(0, 6);
NSRange range2 = NSMakeRange(2, 7);
NSRange intersectionRange = NSIntersectionRange(range1, range2);
// {.location=2, .length=4}
-~~~
+```
> - `NSUnionRange`: Returns the union of the specified ranges. A range covering all indices in and between range1 and range2. If one range is completely contained in the other, the returned range is equal to the larger range.
-~~~{objective-c}
+```objc
NSRange range1 = NSMakeRange(0, 6);
NSRange range2 = NSMakeRange(2, 7);
NSRange unionRange = NSUnionRange(range1, range2);
// {.location=0, .length=9}
-~~~
+```
-### Converting Between NSString * & NSRange
+### Converting Between NSString \* & NSRange
> - `NSStringFromRange`: Returns a string representation of a range.
-~~~{objective-c}
+```objc
NSRange range = NSMakeRange(3, 4);
NSString *string = NSStringFromRange(range); // @"{3,4}"
-~~~
+```
> - `NSRangeFromString`: Returns a range from a textual representation.
-~~~{objective-c}
+```objc
NSString *string = @"{1,5}";
NSRange range = NSRangeFromString(string);
// {.location=1, .length=5}
-~~~
+```
If the string passed into `NSRangeFromString` does not represent a valid range, it will return a range with its location and length set to `0`.
-~~~{objective-c}
+```objc
NSString *string = @"invalid";
NSRange range = NSRangeFromString(string);
// {.location=0, .length=0}
-~~~
+```
While one might be tempted to use `NSStringFromRange` to box `NSRange` for inclusion within an `NSArray`, `NSValue +valueWithRange:` is the way to go:
-~~~{objective-c}
+```objc
NSRange range = NSMakeRange(0, 3);
NSValue *value = [NSValue valueWithRange:range];
-~~~
+```
-* * *
+---
`NSRange` is one of the few cases where some of the underlying implementation of its functions are actually exposed and inlined in the public headers:
#### Foundation/NSRange.h
-~~~{objective-c}
+```objc
NS_INLINE NSRange NSMakeRange(NSUInteger loc, NSUInteger len) {
NSRange r;
r.location = loc;
@@ -200,7 +200,7 @@ NS_INLINE BOOL NSLocationInRange(NSUInteger loc, NSRange range) {
NS_INLINE BOOL NSEqualRanges(NSRange range1, NSRange range2) {
return (range1.location == range2.location && range1.length == range2.length);
}
-~~~
+```
## NSRangePointer
@@ -208,13 +208,13 @@ One oddity worth mentioning with `NSRange` is the existence of `NSRangePointer`.
#### Foundation/NSRange.h
-~~~{objective-c}
+```objc
typedef NSRange *NSRangePointer;
-~~~
+```
So. Without a definitive origin story, one would have to assume that this type was created by a well-meaning framework engineer who noted the confusion around `NSRange` being a struct and not a class. `NSRange *` is equivalent to `NSRangePointer`, though the latter can be found in out parameters for various methods throughout Foundation. `NSAttributedString`, for instance, has an `NSRangePointer` parameter for returning the effective range of an attribute at a particular index (since the attribute likely starts and ends before outside of the specified index):
-~~~{objective-c}
+```objc
NSMutableAttributedString *mutableAttributedString = ...;
NSRange range;
if ([mutableAttributedString attribute:NSUnderlineStyleAttributeName
@@ -226,17 +226,17 @@ if ([mutableAttributedString attribute:NSUnderlineStyleAttributeName
value:[UIColor blueColor]
range:range];
}
-~~~
+```
## CFRange
One final caveat: Core Foundation also defines a `CFRange` type, which differs from `NSRange` in using `CFIndex` types for its members, and having only the function `CFRangeMake`:
-~~~{objective-c}
+```objc
typedef struct {
CFIndex location;
CFIndex length;
} CFRange;
-~~~
+```
Anyone working with CoreText or another low-level C API is likely to encounter `CFRange` in place of `NSRange`.
diff --git a/2014-01-20-extended-file-attributes.md b/2014-01-20-extended-file-attributes.md
index 5aa09ab8..45805ddd 100644
--- a/2014-01-20-extended-file-attributes.md
+++ b/2014-01-20-extended-file-attributes.md
@@ -1,6 +1,6 @@
---
title: Extended File Attributes
-author: Mattt Thompson
+author: Mattt
category: Objective-C
excerpt: "Amidst revelations of widespread spying by the NSA, the concept of metadata has taken an unexpected role in the national conversation about government surveillance. What is it? And how much does it reveal about us and our daily habits? These are questions that the American people are asking, and they deserve an answer."
status:
@@ -15,11 +15,11 @@ Acting with a sense of civic and patriotic duty rivaled only by Uncle Sam wallop
For every file on a UNIX filesystem, there is associated metadata. Indeed, having a path, permissions, and timestamp attributes is what makes a file a file, rather than just a blob of data.
-However, on OS X and iOS, additional metadata can be stored in [**extended file attributes**](http://en.wikipedia.org/wiki/Extended_file_attributes). Introduced in OS X Tiger, they are perfect for associating small, application-specific data with a file. EAs are stored in the attributes B*-Tree of the HFS+ filesystem, and have a maximum size of 128KB as of OS X Lion & iOS 5.
+However, on OS X and iOS, additional metadata can be stored in [**extended file attributes**](https://en.wikipedia.org/wiki/Extended_file_attributes). Introduced in OS X Tiger, they are perfect for associating small, application-specific data with a file. EAs are stored in the attributes B*-Tree of the HFS+ filesystem, and have a maximum size of 128KB as of OS X Lion & iOS 5.
What kind of information, you ask? Invoke the `ls` command in the terminal and pass the `@` option to see what information hides in plain sight.
-~~~
+```
$ ls -l@
-rw-r--r--@ 1 mattt staff 12292 Oct 19 05:59 .DS_Store
com.apple.FinderInfo 32
@@ -29,7 +29,7 @@ $ ls -l@
-rw-r--r--@ 1 mattt staff 1438 Dec 18 14:31 Podfile
com.macromates.selectionRange 4
com.macromates.visibleIndex 1
-~~~
+```
- Finder stores 32 bytes of information in `.DS_Store`, though for reasons that aren't entirely clear.
- Xcode takes 15 bytes to denote the TextEncoding to use for a particular file.
@@ -37,16 +37,16 @@ $ ls -l@
The extended attributes API, declared in ``, has functions for getting, setting, listing, and removing attributes:
-~~~{objective-c}
+```objc
ssize_t getxattr(const char *path, const char *name, void *value, size_t size, u_int32_t position, int options);
int setxattr(const char *path, const char *name, void *value, size_t size, u_int32_t position, int options);
ssize_t listxattr(const char *path, char *namebuf, size_t size, int options);
int removexattr(const char *path, const char *name, int options);
-~~~
+```
-To show these in action, consider the use of extended attributes to associate an [HTTP Etag](http://en.wikipedia.org/wiki/HTTP_ETag) with a file:
+To show these in action, consider the use of extended attributes to associate an [HTTP Etag](https://en.wikipedia.org/wiki/HTTP_ETag) with a file:
-~~~{objective-c}
+```objc
NSHTTPURLResponse *response = ...;
NSURL *fileURL = ...;
@@ -54,11 +54,11 @@ const char *filePath = [fileURL fileSystemRepresentation];
const char *name = "com.Example.Etag";
const char *value = [[response allHeaderFields][@"Etag"] UTF8String];
int result = setxattr(filePath, name, value, strlen(value), 0, 0);
-~~~
+```
As another example, previous to iOS 5.0.1, EAs were the designated way to denote that a particular file should not be synchronized with iCloud (as of iOS 5.1, `NSURL -setResourceValue:forKey:error:` is used, which sets the `com.apple.metadata:com_apple_backup_excludeItem` EA instead):
-~~~{objective-c}
+```objc
#include
if (!&NSURLIsExcludedFromBackupKey) {
@@ -74,7 +74,7 @@ if (!&NSURLIsExcludedFromBackupKey) {
forKey:NSURLIsExcludedFromBackupKey
error:&error];
}
-~~~
+```
Lest extended attributes veer dangerously close to "being a hammer that makes everything look like a nail", let it be made clear: **extended attributes should not be used for critical data**. Not all volume formats support extended attributes, so copying between, say, HFS+ and FAT32 may result in a loss of information. Also consider that nothing is stopping any application from deleting or overwriting extended attributes at any time.
diff --git a/2014-01-27-stewardship.md b/2014-01-27-stewardship.md
index 06c24e83..88a98be6 100644
--- a/2014-01-27-stewardship.md
+++ b/2014-01-27-stewardship.md
@@ -1,14 +1,14 @@
---
title: Stewardship
-author: Mattt Thompson
-category: ""
+author: Mattt
+category: Miscellaneous
tags: nshipster
excerpt: "Stewardship is an old word. It evokes the ethic of public service and duty. To be a steward is to embody the responsibilities that come with ownership. It is an act that justifies authority through continued accountability; both the greatest challenge and reward of creating and maintaining a project."
status:
- swift: n/a
+ swift: n/a
---
-Open Source communities function within what economists describe as a [Gift Economy](http://en.wikipedia.org/wiki/Gift_economy). Rather than paying one another for goods or services through barter or currency, everyone shares freely with one another, and gains [social currency](http://en.wikipedia.org/wiki/Whuffie) based on their generosity. It's similar to how friends tend to take turns inviting one another over for dinner or a party.
+Open Source communities function within what economists describe as a [Gift Economy](https://en.wikipedia.org/wiki/Gift_economy). Rather than paying one another for goods or services through barter or currency, everyone shares freely with one another, and gains [social currency](https://en.wikipedia.org/wiki/Whuffie) based on their generosity. It's similar to how friends tend to take turns inviting one another over for dinner or a party.
With the negligible cost of distributing software over the Internet, developers are able to participate with millions of others around the world. And as a result, we have been able to collaboratively build amazing software.
@@ -60,30 +60,30 @@ CocoaPods is the de facto dependency manager for integrating third party code in
#### NSHipsterKit.podspec
-~~~{ruby}
+```ruby
Pod::Spec.new do |s|
s.name = 'NSHipsterKit'
s.version = '1.0.0'
s.license = 'MIT'
s.summary = "A pretty obscure library.
You've probably never heard of it."
- s.homepage = 'http://nshipster.com'
- s.authors = { 'Mattt Thompson' =>
+ s.homepage = 'https://nshipster.com'
+ s.authors = { 'Mattt' =>
'mattt@nshipster.com' }
s.social_media_url = "https://twitter.com/mattt"
s.source = { :git => 'https://github.com/nshipster/NSHipsterKit.git', :tag => '1.0.0' }
s.source_files = 'NSHipsterKit'
end
-~~~
+```
Once the `.podspec` has been submitted to the CocoaPods specs repository, a consumer would be able to add it to their own project with a Podfile:
#### Podfile
-~~~{ruby}
+```ruby
platform :ios, '7.0'
pod 'NSHipsterKit', '~> 1.0'
-~~~
+```
## Maintaining
@@ -107,7 +107,7 @@ Deviating from these conventions as an author is disrespectful to anyone using t
### Answering Questions
-One of our greatest flaws as humans is our relative inability to comprehend not knowing or understanding something that we ourselves do. This makes it extremely difficult to diagnose (and at times empathize with) misunderstandings that someone else might be having.
+One of our greatest flaws as humans is our relative inability to comprehend not knowing or understanding something that we ourselves do. This makes it extremely difficult to diagnose (and at times empathize with) misunderstandings that someone else might be having.
There's also a slight sadistic tendency for developers to lord knowledge over anyone who doesn't know as much as they do. We had to figure it out for ourselves (uphill both ways, in the snow) so why shouldn't they have to as well?
@@ -138,7 +138,7 @@ In any case, there will come a time when the lights need to be turned off, and i
The alternative is to become a liability, an attractive nuisance... a mockery of what once was a respectable code base.
-* * *
+---
Creating is one of the most fulfilling experiences in life, and it's something that's only improved by sharing with others. As software developers, we have a unique opportunity to be unbounded by physical limitations to help one another.
diff --git a/2014-02-03-mktileoverlay-mkmapsnapshotter-mkdirections.md b/2014-02-03-mktileoverlay-mkmapsnapshotter-mkdirections.md
index 31312a98..599b2f03 100644
--- a/2014-02-03-mktileoverlay-mkmapsnapshotter-mkdirections.md
+++ b/2014-02-03-mktileoverlay-mkmapsnapshotter-mkdirections.md
@@ -1,6 +1,6 @@
---
title: "MKTileOverlay, MKMapSnapshotter & MKDirections"
-author: Mattt Thompson
+author: Mattt
category: Cocoa
excerpt: "Unless you work with MKMapView. on a regular basis, the last you may have heard about the current state of cartography on iOS may not have been under the cheeriest of circumstances. Therefore, it may come as a surprise maps on iOS have gotten quite a bit better in the intervening releases. Quite good, in fact."
status:
@@ -20,19 +20,19 @@ This week on NSHipster, we'll introduce `MKTileOverlay`, `MKMapSnapshotter`, and
Don't like the default Apple Maps tiles? [`MKTileOverlay`](https://developer.apple.com/library/ios/documentation/MapKit/Reference/MKTileOverlay_class/Reference/Reference.html) allows you to seamlessly swap out to another tile set in just a few lines of code.
-> Just like [OpenStreetMap](http://www.openstreetmap.org) and [Google Maps](https://maps.google.com), MKTileOverlay uses [spherical mercator projection (EPSG:3857)](http://en.wikipedia.org/wiki/Mercator_projection#The_spherical_model).
+> Just like [OpenStreetMap](http://www.openstreetmap.org) and [Google Maps](https://maps.google.com), MKTileOverlay uses [spherical mercator projection (EPSG:3857)](https://en.wikipedia.org/wiki/Mercator_projection#The_spherical_model).
### Setting Custom Map View Tile Overlay
-~~~{swift}
+```swift
let template = "http://tile.openstreetmap.org/{z}/{x}/{y}.png"
let overlay = MKTileOverlay(URLTemplate: template)
overlay.canReplaceMapContent = true
mapView.addOverlay(overlay, level: .AboveLabels)
-~~~
-~~~{objective-c}
+```
+```objc
static NSString * const template = @"http://tile.openstreetmap.org/{z}/{x}/{y}.png";
MKTileOverlay *overlay = [[MKTileOverlay alloc] initWithURLTemplate:template];
@@ -40,7 +40,7 @@ overlay.canReplaceMapContent = YES;
[self.mapView addOverlay:overlay
level:MKOverlayLevelAboveLabels];
-~~~
+```
MKTileOverlay is initialized with a URL template string, with the `x` & `y` tile coordinates within the specified zoom level. [MapBox has a great explanation for this scheme is used to generate tiles](https://www.mapbox.com/developers/guide/):
@@ -71,7 +71,7 @@ After setting `canReplaceMapContent` to `YES`, the overlay is added to the `MKMa
In the map view's delegate, `mapView:rendererForOverlay:` is implemented simply to return a new `MKTileOverlayRenderer` instance when called for the `MKTileOverlay` overlay.
-~~~{swift}
+```swift
// MARK: MKMapViewDelegate
func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
@@ -81,8 +81,8 @@ func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOve
return MKTileOverlayRenderer(tileOverlay: tileOverlay)
}
-~~~
-~~~{objective-c}
+```
+```objc
#pragma mark - MKMapViewDelegate
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView
@@ -94,7 +94,7 @@ func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOve
return nil;
}
-~~~
+```
> Speaking of [MapBox](https://www.mapbox.com), [Justin R. Miller](https://github.com/incanus) maintains [MBXMapKit](https://www.mapbox.com/mbxmapkit/), a MapBox-enabled drop-in replacement for `MKMapView`. It's the easiest way to get up-and-running with this world-class mapping service, and highly recommended for anyone looking to make an impact with maps in their next release.
@@ -102,7 +102,7 @@ func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOve
If you need to accommodate a different tile coordinate scheme with your server, or want to add in-memory or offline caching, this can be done by subclassing `MKTileOverlay` and overriding `-URLForTilePath:` and `-loadTileAtPath:result:`:
-~~~{swift}
+```swift
class MKHipsterTileOverlay : MKTileOverlay {
let cache = NSCache()
let operationQueue = NSOperationQueue()
@@ -128,8 +128,8 @@ class MKHipsterTileOverlay : MKTileOverlay {
}
}
}
-~~~
-~~~{objective-c}
+```
+```objc
@interface XXTileOverlay : MKTileOverlay
@property NSCache *cache;
@property NSOperationQueue *operationQueue;
@@ -160,7 +160,7 @@ class MKHipsterTileOverlay : MKTileOverlay {
}
@end
-~~~
+```
## MKMapSnapshotter
@@ -170,7 +170,7 @@ Another addition to iOS 7 was [`MKMapSnapshotter`](https://developer.apple.com/l
### Creating a Map View Snapshot
-~~~{swift}
+```swift
let options = MKMapSnapshotOptions()
options.region = mapView.region
options.size = mapView.frame.size
@@ -188,8 +188,8 @@ snapshotter.startWithCompletionHandler { snapshot, error in
let data = UIImagePNGRepresentation(snapshot.image)
data?.writeToURL(fileURL, atomically: true)
}
-~~~
-~~~{objective-c}
+```
+```objc
MKMapSnapshotOptions *options = [[MKMapSnapshotOptions alloc] init];
options.region = self.mapView.region;
options.size = self.mapView.frame.size;
@@ -208,7 +208,7 @@ MKMapSnapshotter *snapshotter = [[MKMapSnapshotter alloc] initWithOptions:option
NSData *data = UIImagePNGRepresentation(image);
[data writeToURL:fileURL atomically:YES];
}];
-~~~
+```
First, an `MKMapSnapshotOptions` object is created, which is used to specify the region, size, scale, and [camera](https://developer.apple.com/library/mac/documentation/MapKit/Reference/MKMapCamera_class/Reference/Reference.html) used to render the map image.
@@ -220,7 +220,7 @@ However, this only draws the map for the specified region; annotations are rende
Including annotations—or indeed, any additional information to the map snapshot—can be done by dropping down into Core Graphics:
-~~~{swift}
+```swift
snapshotter.startWithQueue(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { snapshot, error in
guard let snapshot = snapshot else {
print("Snapshot error: \(error)")
@@ -249,8 +249,8 @@ snapshotter.startWithQueue(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEF
let data = UIImagePNGRepresentation(compositeImage)
data?.writeToURL(fileURL, atomically: true)
}
-~~~
-~~~{objective-c}
+```
+```objc
[snapshotter startWithQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
completionHandler:^(MKMapSnapshot *snapshot, NSError *error) {
if (error) {
@@ -283,13 +283,13 @@ snapshotter.startWithQueue(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEF
}
UIGraphicsEndImageContext();
}];
-~~~
+```
## MKDirections
The final iOS 7 addition to MapKit that we'll discuss is [`MKDirections`](https://developer.apple.com/library/mac/documentation/MapKit/Reference/MKDirections_class/Reference/Reference.html).
-> `MKDirections`' spiritual predecessor (of sorts), [`MKLocalSearch`](https://developer.apple.com/library/ios/documentation/MapKit/Reference/MKLocalSearch/Reference/Reference.html) was discussed in [a previous NSHipster article](http://nshipster.com/mklocalsearch/)
+> `MKDirections`' spiritual predecessor (of sorts), [`MKLocalSearch`](https://developer.apple.com/library/ios/documentation/MapKit/Reference/MKLocalSearch/Reference/Reference.html) was discussed in [a previous NSHipster article](https://nshipster.com/mklocalsearch/)
As its name implies, `MKDirections` fetches routes between two waypoints. A `MKDirectionsRequest` object is initialized with a `source` and `destination`, and is then passed into an `MKDirections` object, which can calculate several possible routes and estimated travel times.
@@ -299,7 +299,7 @@ Building on the previous example, here is how `MKDirections` might be used to cr
### Getting Snapshots for each Step of Directions on a Map View
-~~~{swift}
+```swift
let request = MKDirectionsRequest()
request.source = MKMapItem.mapItemForCurrentLocation()
request.destination = MKMapItem(...)
@@ -390,8 +390,8 @@ func stepImagesFromDirectionsResponse(response: MKDirectionsResponse, completion
}
}
}
-~~~
-~~~{objective-c}
+```
+```objc
NSMutableArray *mutableStepImages = [NSMutableArray array];
MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init];
@@ -448,7 +448,7 @@ MKDirections *directions = [[MKDirections alloc] initWithRequest:request];
}];
}
}];
-~~~
+```
* * *
diff --git a/2014-02-10-associated-objects.md b/2014-02-10-associated-objects.md
index 9ed63c90..06fb7afa 100644
--- a/2014-02-10-associated-objects.md
+++ b/2014-02-10-associated-objects.md
@@ -1,23 +1,23 @@
---
title: Associated Objects
-author: Mattt Thompson
+author: Mattt
category: Objective-C
excerpt: "Associated Objects is a feature of the Objective-C 2.0 runtime, which allows objects to associate arbitrary values for keys at runtime. It's dark juju, to be handled with as much caution as any other function from objc/runtime.h"
status:
swift: n/a
---
-~~~{objective-c}
+```objc
#import
-~~~
+```
Objective-C developers are conditioned to be wary of whatever follows this ominous incantation. And for good reason: messing with the Objective-C runtime changes the very fabric of reality for all of the code that runs on it.
-In the right hands, the functions of `` have the potential to add powerful new behavior to an application or framework, in ways that would otherwise not be possible. In the wrong hands, it drains the proverbial [sanity meter](http://en.wikipedia.org/wiki/Eternal_Darkness:_Sanity's_Requiem#Sanity_effects) of the code, and everything it may interact with (with [terrifying side-effects](http://www.youtube.com/watch?v=RSXcajQnasc#t=0m30s)).
+In the right hands, the functions of `` have the potential to add powerful new behavior to an application or framework, in ways that would otherwise not be possible. In the wrong hands, it drains the proverbial [sanity meter](https://en.wikipedia.org/wiki/Eternal_Darkness:_Sanity's_Requiem#Sanity_effects) of the code, and everything it may interact with (with [terrifying side-effects](https://www.youtube.com/watch?v=RSXcajQnasc#t=0m30s)).
-Therefore, it is with great trepidation that we consider this [Faustian bargain](http://en.wikipedia.org/wiki/Deal_with_the_Devil), and look at one of the subjects most-often requested by NSHipster readers: associated objects.
+Therefore, it is with great trepidation that we consider this [Faustian bargain](https://en.wikipedia.org/wiki/Deal_with_the_Devil), and look at one of the subjects most-often requested by NSHipster readers: associated objects.
-* * *
+---
Associated Objects—or Associative References, as they were originally known—are a feature of the Objective-C 2.0 runtime, introduced in OS X Snow Leopard (available in iOS 4). The term refers to the following three C functions declared in ``, which allow objects to associate arbitrary values for keys at runtime:
@@ -27,17 +27,11 @@ Associated Objects—or Associative References, as they were originally known—
Why is this useful? It allows developers to **add custom properties to existing classes in categories**, which [is an otherwise notable shortcoming for Objective-C](https://developer.apple.com/library/ios/documentation/cocoa/conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html).
-#### NSObject+AssociatedObject.h
-
-~~~{objective-c}
+```objc
@interface NSObject (AssociatedObject)
@property (nonatomic, strong) id associatedObject;
@end
-~~~
-#### NSObject+AssociatedObject.m
-
-~~~{objective-c}
@implementation NSObject (AssociatedObject)
@dynamic associatedObject;
@@ -48,20 +42,20 @@ Why is this useful? It allows developers to **add custom properties to existing
- (id)associatedObject {
return objc_getAssociatedObject(self, @selector(associatedObject));
}
-~~~
+```
It is often recommended that they key be a `static char`—or better yet, the pointer to one. Basically, an arbitrary value that is guaranteed to be constant, unique, and scoped for use within getters and setters:
-~~~{objective-c}
+```objc
static char kAssociatedObjectKey;
objc_getAssociatedObject(self, &kAssociatedObjectKey);
-~~~
+```
However, a much simpler solution exists: just use a selector.
-
Since SELs are guaranteed to be unique and constant, you can use _cmd as the key for objc_setAssociatedObject(). #objective-c #snowleopard
- @property (assign) or @property (unsafe_unretained)
+ @property (assign) or @property (unsafe_unretained)
Specifies a weak reference to the associated object.
@@ -89,10 +83,10 @@ Values can be associated onto objects according to the behaviors defined by the
Specifies a strong reference to the associated object, and that the association is not made atomically.
@@ -100,10 +94,10 @@ Values can be associated onto objects according to the behaviors defined by the
Specifies that the associated object is copied, and that the association is not made atomically.
@@ -111,10 +105,10 @@ Values can be associated onto objects according to the behaviors defined by the
Specifies a strong reference to the associated object, and that the association is made atomically.
@@ -122,10 +116,10 @@ Values can be associated onto objects according to the behaviors defined by the
Specifies that the associated object is copied, and that the association is made atomically.
@@ -136,32 +130,55 @@ Values can be associated onto objects according to the behaviors defined by the
Weak associations to objects made with `OBJC_ASSOCIATION_ASSIGN` are not zero `weak` references, but rather follow a behavior similar to `unsafe_unretained`, which means that one should be cautious when accessing weakly associated objects within an implementation.
-> According to the Deallocation Timeline described in [WWDC 2011, Session 322](https://developer.apple.com/videos/wwdc/2011/#322-video) (~36:00), associated objects are erased surprisingly late in the object lifecycle, in `object_dispose()`, which is invoked by `NSObject -dealloc`.
+{% info %}
+According to the deallocation timeline described in
+[WWDC 2011, Session 322](https://asciiwwdc.com/2011/sessions/322) (~36:00),
+associated objects are erased surprisingly late in the object lifecycle ---
+`object_dispose()`,
+which is invoked by `NSObject -dealloc`.
+{% endinfo %}
## Removing Values
One may be tempted to call `objc_removeAssociatedObjects()` at some point in their foray into associated objects. However, [as described in the documentation](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html#//apple_ref/c/func/objc_removeAssociatedObjects), it's unlikely that you would have an occasion to invoke it yourself:
->The main purpose of this function is to make it easy to return an object to a "pristine state”. You should not use this function for general removal of associations from objects, since it also removes associations that other clients may have added to the object. Typically you should use objc_setAssociatedObject with a nil value to clear an association.
+> The main purpose of this function is to make it easy to return an object to a "pristine state”. You should not use this function for general removal of associations from objects, since it also removes associations that other clients may have added to the object. Typically you should use objc_setAssociatedObject with a nil value to clear an association.
## Patterns
-- **Adding private variables to facilitate implementation details**. When extending the behavior of a built-in class, it may be necessary to keep track of additional state. This is the _textbook_ use case for associated objects. For example, AFNetworking uses associated objects on its `UIImageView` category to [store a request operation object](https://github.com/AFNetworking/AFNetworking/blob/2.1.0/UIKit%2BAFNetworking/UIImageView%2BAFNetworking.m#L57-L63), used to asynchronously fetch a remote image at a particular URL.
-- **Adding public properties to configure category behavior.** Sometimes, it makes more sense to make category behavior more flexible with a property, than in a method parameter. In these situations, a public-facing property is an acceptable situation to use associated objects. To go back to the previous example of AFNetworking, its category on `UIImageView`, [its `imageResponseSerializer`](https://github.com/AFNetworking/AFNetworking/blob/2.1.0/UIKit%2BAFNetworking/UIImageView%2BAFNetworking.h#L60-L65) allows image views to optionally apply a filter, or otherwise change the rendering of a remote image before it is set and cached to disk.
-- **Creating an associated observer for KVO**. When using [KVO](http://nshipster.com/key-value-observing/) in a category implementation, it is recommended that a custom associated-object be used as an observer, rather than the object observing itself.
+### Adding private variables to facilitate implementation details
+
+When extending the behavior of a built-in class, it may be necessary to keep track of additional state. This is the _textbook_ use case for associated objects.
+
+### Adding public properties to configure category behavior.
+
+Sometimes, it makes more sense to make category behavior more flexible with a property, than in a method parameter. In these situations, a public-facing property is an acceptable situation to use associated objects.
+
+### Creating an associated observer for KVO
+
+When using [KVO](https://nshipster.com/key-value-observing/) in a category implementation, it is recommended that a custom associated-object be used as an observer, rather than the object observing itself.
## Anti-Patterns
-- **Storing an associated object, when the value is not needed**. A common pattern for views is to create a convenience method that populates fields and attributes based on a model object or compound value. If that value does not need to be recalled later, it is acceptable, and indeed preferable, not to associate with that object.
-- **Storing an associated object, when the value can be inferred.** For example, one might be tempted to store a reference to a custom accessory view's containing `UITableViewCell`, for use in `tableView:accessoryButtonTappedForRowWithIndexPath:`, when this can retrieved by calling `cellForRowAtIndexPath:`.
-- **Using associated objects instead of X**, where X is any one the following:
- - [Subclassing](https://developer.apple.com/library/ios/documentation/cocoa/conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html) for when inheritance is a more reasonable fit than composition.
- - [Target-Action](https://developer.apple.com/library/ios/documentation/general/conceptual/Devpedia-CocoaApp/TargetAction.html) for adding interaction events to responders.
- - [Gesture Recognizers](https://developer.apple.com/library/ios/documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/GestureRecognizer_basics/GestureRecognizer_basics.html) for any situations when target-action doesn't suffice.
- - [Delegation](https://developer.apple.com/library/ios/documentation/general/conceptual/DevPedia-CocoaCore/Delegation.html) when behavior can be delegated to another object.
- - [NSNotification & NSNotificationCenter](http://nshipster.com/nsnotification-and-nsnotificationcenter/) for communicating events across a system in a loosely-coupled way.
+### Storing an associated object, when the value is not needed
-* * *
+A common pattern for views is to create a convenience method that populates fields and attributes based on a model object or compound value. If that value does not need to be recalled later, it is acceptable, and indeed preferable, not to associate with that object.
+
+### Storing an associated object, when the value can be inferred
+
+For example, one might be tempted to store a reference to a custom accessory view's containing `UITableViewCell`, for use in `tableView:accessoryButtonTappedForRowWithIndexPath:`, when this can retrieved by calling `cellForRowAtIndexPath:`.
+
+### Using associated objects instead of _X_
+
+...where X is any one the following:
+
+ - [Subclassing](https://developer.apple.com/library/ios/documentation/cocoa/conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html) for when inheritance is a more reasonable fit than composition.
+ - [Target-Action](https://developer.apple.com/library/ios/documentation/general/conceptual/Devpedia-CocoaApp/TargetAction.html) for adding interaction events to responders.
+ - [Gesture Recognizers](https://developer.apple.com/library/ios/documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/GestureRecognizer_basics/GestureRecognizer_basics.html) for any situations when target-action doesn't suffice.
+ - [Delegation](https://developer.apple.com/library/ios/documentation/general/conceptual/DevPedia-CocoaCore/Delegation.html) when behavior can be delegated to another object.
+ - [NSNotification & NSNotificationCenter](https://nshipster.com/nsnotification-and-nsnotificationcenter/) for communicating events across a system in a loosely-coupled way.
+
+---
Associated objects should be seen as a method of last resort, rather than a solution in search of a problem (and really, categories themselves really shouldn't be at the top of the toolchain to begin with).
diff --git a/2014-02-17-method-swizzling.md b/2014-02-17-method-swizzling.md
index c4caca87..b3c49093 100644
--- a/2014-02-17-method-swizzling.md
+++ b/2014-02-17-method-swizzling.md
@@ -1,6 +1,6 @@
---
title: Method Swizzling
-author: Mattt Thompson
+author: Mattt
category: Objective-C
excerpt: "Method swizzling is the process of changing the implementation of an existing selector. It's a technique made possible by the fact that method invocations in Objective-C can be changed at runtime, by changing how selectors are mapped to underlying functions in a class's dispatch table."
status:
@@ -18,9 +18,9 @@ status:
> Would you do it?
> And so we cannot know ourselves or what we'd really do...
> With all your power ... What would you do?
-> The Flaming Lips, "The Yeah Yeah Yeah Song (With All Your Power)"
+> The Flaming Lips, "The Yeah Yeah Yeah Song (With All Your Power)"
-In last week's article about [associated objects](http://nshipster.com/associated-objects/), we began to explore the dark arts of the Objective-C runtime. This week, we venture further, to discuss what is perhaps the most contentious of runtime hackery techniques: method swizzling.
+In last week's article about [associated objects](https://nshipster.com/associated-objects/), we began to explore the dark arts of the Objective-C runtime. This week, we venture further, to discuss what is perhaps the most contentious of runtime hackery techniques: method swizzling.
* * *
@@ -30,9 +30,10 @@ For example, let's say we wanted to track how many times each view controller is
Each view controller could add tracking code to its own implementation of `viewDidAppear:`, but that would make for a ton of duplicated boilerplate code. Subclassing would be another possibility, but it would require subclassing `UIViewController`, `UITableViewController`, `UINavigationController`, and every other view controller class—an approach that would also suffer from code duplication.
-Fortunately, there is another way: **method swizzling** from a category. Here's how to do it:
+Fortunately, there is another way: **method swizzling** from a category.
+Here's how to do it:
-~~~{objective-c}
+```objc
#import
@implementation UIViewController (Tracking)
@@ -49,25 +50,21 @@ Fortunately, there is another way: **method swizzling** from a category. Here's
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
// When swizzling a class method, use the following:
- // Class class = object_getClass((id)self);
- // ...
// Method originalMethod = class_getClassMethod(class, originalSelector);
// Method swizzledMethod = class_getClassMethod(class, swizzledSelector);
- BOOL didAddMethod =
- class_addMethod(class,
- originalSelector,
- method_getImplementation(swizzledMethod),
- method_getTypeEncoding(swizzledMethod));
+ IMP originalImp = method_getImplementation(originalMethod);
+ IMP swizzledImp = method_getImplementation(swizzledMethod);
- if (didAddMethod) {
- class_replaceMethod(class,
+ class_replaceMethod(class,
swizzledSelector,
- method_getImplementation(originalMethod),
+ originalImp,
method_getTypeEncoding(originalMethod));
- } else {
- method_exchangeImplementations(originalMethod, swizzledMethod);
- }
+ class_replaceMethod(class,
+ originalSelector,
+ swizzledImp,
+ method_getTypeEncoding(swizzledMethod));
+
});
}
@@ -79,9 +76,7 @@ Fortunately, there is another way: **method swizzling** from a category. Here's
}
@end
-~~~
-
-> In computer science, [pointer swizzling](http://en.wikipedia.org/wiki/Pointer_swizzling) is the conversion of references based on name or position to direct pointer references. While the origins of Objective-C's usage of the term are not entirely known, it's understandable why it was co-opted, since method swizzling involves changing the reference of a function pointer by its selector.
+```
Now, when any instance of `UIViewController`, or one of its subclasses invokes `viewWillAppear:`, a log statement will print out.
@@ -89,6 +84,19 @@ Injecting behavior into the view controller lifecycle, responder events, view dr
Regardless of _why_ or _where_ one chooses to use swizzling, the _how_ remains absolute:
+{% info %}
+
+In computer science,
+[pointer swizzling](https://en.wikipedia.org/wiki/Pointer_swizzling)
+is the conversion of references based on name or position
+to direct pointer references.
+While the origins of Objective-C's usage of the term are not entirely known,
+it's understandable why it was co-opted,
+since method swizzling involves changing the reference of a function pointer
+by its selector.
+
+{% endinfo %}
+
## +load vs. +initialize
**Swizzling should always be done in `+load`.**
@@ -101,7 +109,7 @@ Because method swizzling affects global state, it is important to minimize the p
**Swizzling should always be done in a `dispatch_once`.**
-Again, because swizzling changes global state, we need to take every precaution available to us in the runtime. Atomicity is one such precaution, as is a guarantee that code will be executed exactly once, even across different threads. Grand Central Dispatch's `dispatch_once` provides both of these desirable behaviors, and should be considered as much a standard practice for swizzling as they are for [initializing singletons](http://nshipster.com/c-storage-classes/).
+Again, because swizzling changes global state, we need to take every precaution available to us in the runtime. Atomicity is one such precaution, as is a guarantee that code will be executed exactly once, even across different threads. Grand Central Dispatch's `dispatch_once` provides both of these desirable behaviors, and should be considered as much a standard practice for swizzling as they are for [initializing singletons](https://nshipster.com/c-storage-classes/).
## Selectors, Methods, & Implementations
@@ -121,28 +129,42 @@ To swizzle a method is to change a class's dispatch table in order to resolve me
It may appear that the following code will result in an infinite loop:
-~~~{objective-c}
+```objc
- (void)xxx_viewWillAppear:(BOOL)animated {
[self xxx_viewWillAppear:animated];
NSLog(@"viewWillAppear: %@", NSStringFromClass([self class]));
}
-~~~
+```
Surprisingly, it won't. In the process of swizzling, `xxx_viewWillAppear:` has been reassigned to the original implementation of `UIViewController -viewWillAppear:`. It's good programmer instinct for calling a method on `self` in its own implementation to raise a red flag, but in this case, it makes sense if we remember what's _really_ going on. However, if we were to call `viewWillAppear:` in this method, it _would_ cause an infinite loop, since the implementation of this method will be swizzled to the `viewWillAppear:` selector at runtime.
-> Remember to prefix your swizzled method name, the same way you might any other contentious category method.
+{% warning %}
+
+Remember to prefix your swizzled method name
+as you would any other contentious category method.
+
+{% endwarning %}
## Considerations
Swizzling is widely considered a voodoo technique, prone to unpredictable behavior and unforeseen consequences. While it is not the safest thing to do, method swizzling is reasonably safe, when the following precautions are taken:
-- **Always invoke the original implementation of a method (unless you have a good reason not to)**: APIs provide a contract for input and output, but the implementation in-between is a black box. Swizzling a method and not calling the original implementation may cause underlying assumptions about private state to break, along with the rest of your application.
-- **Avoid collisions**: Prefix category methods, and make damn well sure that nothing else in your code base (or any of your dependencies) are monkeying around with the same piece of functionality as you are.
-- **Understand what's going on**: Simply copy-pasting swizzling code without understanding how it works is not only dangerous, but is a wasted opportunity to learn a lot about the Objective-C runtime. Read through the [Objective-C Runtime Reference](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html#//apple_ref/c/func/method_getImplementation) and browse `` to get a good sense of how and why things happen. _Always endeavor to replace magical thinking with understanding._
-- **Proceed with caution**: No matter how confident you are about swizzling Foundation, UIKit, or any other built-in framework, know that everything could break in the next release. Be ready for that, and go the extra mile to ensure that in playing with fire, you don't get `NSBurned`.
+### Always invoke the original implementation of a method (unless you have a good reason not to)
+
+APIs provide a contract for input and output, but the implementation in-between is a black box. Swizzling a method and not calling the original implementation may cause underlying assumptions about private state to break, along with the rest of your application.
+
+### Avoid collisions
+
+Prefix category methods, and make damn well sure that nothing else in your code base (or any of your dependencies) are monkeying around with the same piece of functionality as you are.
+
+### Understand what's going on
+
+Simply copy-pasting swizzling code without understanding how it works is not only dangerous, but is a wasted opportunity to learn a lot about the Objective-C runtime. Read through the [Objective-C Runtime Reference](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html#//apple_ref/c/func/method_getImplementation) and browse `` to get a good sense of how and why things happen. _Always endeavor to replace magical thinking with understanding._
+
+### Proceed with caution
-> Feeling gun shy about invoking the Objective-C runtime directly? [Jonathan ‘Wolf’ Rentzsch](https://twitter.com/rentzsch) provides a battle-tested, CocoaPods-ready library called [JRSwizzle](https://github.com/rentzsch/jrswizzle) that will take care of everything for you.
+No matter how confident you are about swizzling Foundation, UIKit, or any other built-in framework, know that everything could break in the next release. Be ready for that, and go the extra mile to ensure that in playing with fire, you don't get `NSBurned`.
* * *
-Like [associated objects](http://nshipster.com/associated-objects/), method swizzling is a powerful technique when you need it, but should be used sparingly.
+Like [associated objects](/associated-objects/), method swizzling is a powerful technique when you need it, but should be used sparingly.
diff --git a/2014-02-24-namespacing.md b/2014-02-24-namespacing.md
index af5bb98a..510f3fcf 100644
--- a/2014-02-24-namespacing.md
+++ b/2014-02-24-namespacing.md
@@ -1,6 +1,6 @@
---
title: Namespacing
-author: Mattt Thompson
+author: Mattt
category: Objective-C
excerpt: "Namespacing is the preeminent bugbear of Objective-C. A cosmetic quirk with global implications, the language's lack of identifier containers remains a source of prodigious quantities of caremad for armchair language critics."
status:
@@ -15,9 +15,9 @@ Like a parent faced with the task of explaining the concept of death or the non-
> Why, Jimmy, `NS` stands for `NeXTSTEP` (well, actually, `NeXTSTEP/Sun`, but we'll cover that with "the birds & the bees" talk), and it's used to...
-...but by the time the words have left your mouth, you can already sense the disappointment in their face. Their innocence has been lost, and with an audible *sigh* of resignation, they start to ask uncomfortable questions about [@](http://nshipster.com/at-compiler-directives/)
+...but by the time the words have left your mouth, you can already sense the disappointment in their face. Their innocence has been lost, and with an audible _sigh_ of resignation, they start to ask uncomfortable questions about [@](https://nshipster.com/at-compiler-directives/)
-* * *
+---
Namespacing is the preeminent bugbear of Objective-C. A cosmetic quirk with global implications, the language's lack of identifier containers remains a source of prodigious quantities of caremad for armchair language critics.
@@ -33,16 +33,16 @@ As noted many times in this publication, Objective-C is built directly on top of
You can see this for yourself—try defining a new static variable with the same name as an existing `@interface`, and the compiler will generate an error:
-~~~{objective-c}
+```objc
@interface XXObject : NSObject
@end
static char * XXObject; // Redefinition of "XXObject" as different kind of symbol
-~~~
+```
That said, the Objective-C runtime creates a layer of abstraction on top of the C type system, allowing the following code to compile without even a snicker:
-~~~{objective-c}
+```objc
@protocol Malkovich
@end
@@ -66,7 +66,7 @@ That said, the Objective-C runtime creates a layer of abstraction on top of the
return Malkovich;
}
@end
-~~~
+```
Within the context of the Objective-C runtime, a program is able to differentiate between a class, a protocol, a category, an instance variable, an instance method, and a class method all having the same name.
@@ -90,43 +90,43 @@ A veteran Mac or iOS developer will have likely memorized most if not all of the
-
AB
AddressBook / AddressBookUI
-
AC
Accounts
-
AD
iAd
-
AL
AssetsLibrary
-
AU
AudioUnit
-
AV
AVFoundation
-
CA
CoreAnimation
-
CB
CoreBluetooth
-
CF
CoreFoundation / CFNetwork
-
CG
CoreGraphics / QuartzCore / ImageIO
-
CI
CoreImage
-
CL
CoreLocation
-
CM
CoreMedia / CoreMotion
-
CV
CoreVideo
-
EA
ExternalAccessory
-
EK
EventKit / EventKitUI
-
GC
GameController
-
GLK*
GLKit
-
JS
JavaScriptCore
-
MA
MediaAccessibility
-
MC
MultipeerConnectivity
-
MF
MessageUI*
-
MIDI*
CoreMIDI
-
MK
MapKit
-
MP
MediaPlayer
-
NK
NewsstandKit
-
NS
Foundation, AppKit, CoreData
-
PK
PassKit
-
QL
QuickLook
-
SC
SystemConfiguration
-
Sec*
Security*
-
SK
StoreKit / SpriteKit
-
SL
Social
-
SS
Safari Services
-
TW
Twitter
-
UI
UIKit
-
UT
MobileCoreServices
+
AB
AddressBook / AddressBookUI
+
AC
Accounts
+
AD
iAd
+
AL
AssetsLibrary
+
AU
AudioUnit
+
AV
AVFoundation
+
CA
CoreAnimation
+
CB
CoreBluetooth
+
CF
CoreFoundation / CFNetwork
+
CG
CoreGraphics / QuartzCore / ImageIO
+
CI
CoreImage
+
CL
CoreLocation
+
CM
CoreMedia / CoreMotion
+
CV
CoreVideo
+
EA
ExternalAccessory
+
EK
EventKit / EventKitUI
+
GC
GameController
+
GLK*
GLKit
+
JS
JavaScriptCore
+
MA
MediaAccessibility
+
MC
MultipeerConnectivity
+
MF
MessageUI*
+
MIDI*
CoreMIDI
+
MK
MapKit
+
MP
MediaPlayer
+
NK
NewsstandKit
+
NS
Foundation, AppKit, CoreData
+
PK
PassKit
+
QL
QuickLook
+
SC
SystemConfiguration
+
Sec*
Security*
+
SK
StoreKit / SpriteKit
+
SL
Social
+
SS
Safari Services
+
TW
Twitter
+
UI
UIKit
+
UT
MobileCoreServices
@@ -144,23 +144,23 @@ Because of this, many established libraries still use 2-letter prefixes. Conside
-
Seeing as how [we're already seeing prefix overlap among 3rd-party libraries](https://github.com/AshFurrow/AFTabledCollectionView), make sure that you follow a 3+-letter convention in your own code.
-> For especially future-focused library authors, consider using [`@compatibility_alias`](http://nshipster.com/at-compiler-directives/) to provide a seamless migration path for existing users in your next major upgrade.
+> For especially future-focused library authors, consider using [`@compatibility_alias`](https://nshipster.com/at-compiler-directives/) to provide a seamless migration path for existing users in your next major upgrade.
### Method Prefixes
@@ -168,21 +168,21 @@ It's not just classes that are prone to naming collisions: selectors suffer from
Consider the category:
-~~~{objective-c}
+```objc
@interface NSString (PigLatin)
- (NSString *)pigLatinString;
@end
-~~~
+```
If `-pigLatinString` were implemented by another category (or added to the `NSString` class in a future version of iOS or OS X), any calls to that method would result in undefined behavior, since no guarantee is made as to the order in which methods are defined by the runtime.
This can be guarded against by prefixing the method name, just like the class name (prefixing the category name isn't a bad idea, either):
-~~~{objective-c}
+```objc
@interface NSString (XXXPigLatin)
- (NSString *)xxx_pigLatinString;
@end
-~~~
+```
Apple's recommendation that [all category methods use prefixes](https://developer.apple.com/library/ios/documentation/cocoa/conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html#//apple_ref/doc/uid/TP40011210-CH6-SW4) is even less widely known or accepted than its policy on class prefixing.
@@ -196,9 +196,9 @@ Just as with constitutional scholarship, there will be strict and loose interpre
#### Swizzling
-The one case where method prefixing (or suffixing) is absolutely necessary is when doing method replacement, as discussed in last week's article on [swizzling](http://nshipster.com/method-swizzling/).
+The one case where method prefixing (or suffixing) is absolutely necessary is when doing method replacement, as discussed in last week's article on [swizzling](https://nshipster.com/method-swizzling/).
-~~~{objective-c}
+```objc
@implementation UIViewController (Swizzling)
- (void)xxx_viewDidLoad {
@@ -206,19 +206,19 @@ The one case where method prefixing (or suffixing) is absolutely necessary is wh
// Swizzled implementation
}
-~~~
+```
## Do We _Really_ Need Namespaces?
With all of the recent talk about replacing / reinventing / reimagining Objective-C, it's almost taken as a given that namespacing would be an obvious feature. But what does that actually get us?
-**Aesthetics?** Aside from IETF members and military personnel, nobody likes the visual aesthetic of CLAs. But would `::`, `/`, or an extra `.` really make matters better? Do we _really_ want to start calling `NSArray` "Foundation Array"? (And what would I do with NSHipster.com ?!)
+**Aesthetics?** Aside from IETF members and military personnel, nobody likes the visual aesthetic of CLAs. But would `::`, `/`, or an extra `.` really make matters better? Do we _really_ want to start calling `NSArray` "Foundation Array"? (And what would I do with NSHipster.com ?!)
**Semantics?** Start to look closely at any other language, and how they actually use namespaces, and you'll realize that namespaces don't magically solve all matters of ambiguity. If anything, the additional context makes things worse.
Not to create a straw man, but an imagined implementation of Objective-C namespaces probably look a lot like this:
-~~~{objective-c}
+```objc
@namespace XX
@implementation Object
@@ -227,12 +227,12 @@ Not to create a straw man, but an imagined implementation of Objective-C namespa
- (void)foo {
F:Array *array = @[@1,@2, @3];
- // ...
+ <#...#>
}
@end
@end
-~~~
+```
What we have currently—warts and all—has the notable advantage of non-ambiguity. There is no mistaking `NSString` for anything other than what it is, either by the compiler or when we talk about it as developers. There are no special contextual considerations to consider when reading through code to understand what actors are at play. And best of all: class names are [_exceedingly_ easy to search for](http://lmgtfy.com/?q=NSString).
diff --git a/2014-03-03-nstemporarydirectory.md b/2014-03-03-nstemporarydirectory.md
deleted file mode 100644
index 429b8e02..00000000
--- a/2014-03-03-nstemporarydirectory.md
+++ /dev/null
@@ -1,178 +0,0 @@
----
-title: "NSTemporaryDirectory / NSItemReplacementDirectory / mktemp(3)"
-author: Mattt Thompson
-category: Cocoa
-excerpt: "Volumes have been written about persisting data, but when it comes to short-lived, temporary files, there is very little to go on for Objective-C. (Or if there has, perhaps it was poetically ephemeral itself)."
-revisions:
- "2016-03-10": Translated sample code into Swift.
-status:
- swift: 2.1.1
- reviewed: March 10, 2016
----
-
-Volumes have been written about persisting data, but when it comes to short-lived, temporary files, there is very little to go on for Objective-C. (Or if there has, perhaps it was poetically ephemeral itself).
-
-* * *
-
-Temporary files are used to write a buffer to disk, to either be atomically moved to a permanent location, or processed in some manner and then discarded. Creating a temporary file involves finding the appropriate part of the filesystem, generating a unique name, and moving or deleting the file after you're finished using it.
-
-## Finding an Enclosing Directory
-
-The first step to creating temporary files or directories is to find a reasonable, out-of-the-way place to write to—somewhere that won't be backed up by Time Machine or synced to iCloud or the like.
-
-On Unix systems, the `/tmp` directory was the de facto scratch space, but with the sandboxed containers of iOS and OS X apps today, a hard-coded path just won't cut it.
-
-`NSTemporaryDirectory` is a Foundation function that returns the directory designated for writing short-lived files on the targeted platform.
-
-### A Wild Goose Chase
-
-In recent years, Apple has pushed to extricate filesystem path operations from `NSString` APIs, recommending that users switch to using `NSURL` and `NSURL`-based APIs for classes like `NSFileManager`. Unfortunately, the migration has not been entirely smooth.
-
-Consider the documentation for `NSTemporaryDirectory`:
-
-> See the `NSFileManager` method `URLForDirectory:inDomain:appropriateForURL:create:error:` for the preferred means of finding the correct temporary directory.
-
-Alright, fair enough. Let's see what's going on with `NSFileManager -URLForDirectory:inDomain:appropriateForURL:create:error:`:
-
-> You can also use this method to create a new temporary directory for storing things like autosave files; to do so, specify `NSItemReplacementDirectory` for the directory parameter, `NSUserDomainMask` for the `domain` parameter, and a valid parent directory for the `url` parameter. After locating (or creating) the desired directory, this method returns the URL for that directory.
-
-Huh? Even after reading through that a few times, it's still unclear how to use this, or what the expected behavior is. A quick search through the mailing lists [reaffirms](http://lists.apple.com/archives/cocoa-dev/2012/Apr/msg00117.html) this [confusion](http://lists.apple.com/archives/cocoa-dev/2012/Feb/msg00186.html).
-
-_Actually_, this method appears to be intended for moving _existing_ temporary files to a permanent location on disk with `-replaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error:`. Not exactly what we're looking for.
-
-So much for the `NSString` filesystem API migration. Let's stick to something that works:
-
-~~~{swift}
-NSURL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
-~~~
-~~~{objective-c}
-[NSURL fileURLWithPath:NSTemporaryDirectory() isDirectory:YES];
-~~~
-
-## Generating a Unique Directory or File Name
-
-With a place to call home (temporarily), the next step is to figure out what to name our temporary file. We don't really care what temporary files are named—the only real concern is that they're unique, so as to not interfere with, or be interfered by, any other temporary files.
-
-The best way to generate a unique identifier is to use the `globallyUniqueString` method on `NSProcessInfo`
-
-~~~{swift}
-let identifier = NSProcessInfo.processInfo().globallyUniqueString
-~~~
-~~~{objective-c}
-NSString *identifier = [[NSProcessInfo processInfo] globallyUniqueString];
-~~~
-
-This will return a string in the format: `5BD255F4-CA55-4B82-A555-0F4BC5CA2AD6-479-0000018E14D059CC`
-
-> Other sources advise the direct invocation of the `mktemp(3)` system command in order to mitigate potential conflicts. However, using `NSProcessInfo -globallyUniqueString` to generate unique names is extremely unlikely to result in a collision.
-
-Alternatively, `NSUUID` ([discussed previously](http://nshipster.com/uuid-udid-unique-identifier)) also produces workable results, assuming that you're not doing anything _too_ crazy.
-
-~~~{swift}
-NSUUID().UUIDString
-~~~
-~~~{objective-c}
-[[NSUUID UUID] UUIDString]
-~~~
-
-This produces a string in the format: `22361D15-E17B-4C48-AEA6-C73BBEA17011`
-
-## Creating a Temporary File Path
-
-Using the aforementioned technique for generating unique identifiers, we can create unique temporary file paths:
-
-~~~{swift}
-let fileName = String(format: "%@_%@", NSProcessInfo.processInfo().globallyUniqueString, "file.txt")
-let fileURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).URLByAppendingPathComponent(fileName)
-~~~
-~~~{objective-c}
-NSString *fileName = [NSString stringWithFormat:@"%@_%@", [[NSProcessInfo processInfo] globallyUniqueString], @"file.txt"];
-NSURL *fileURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:fileName]];
-~~~
-
-## Creating a Temporary Directory
-
-In situations where many temporary files might be created by a process, it may be a good idea to create a temporary sub-directory, which could then be removed for easy cleanup.
-
-Creating a temporary directory is no different than any other invocation of `NSFileManager -createDirectoryAtURL:withIntermediateDirectories:attributes:error:`:
-
-~~~{swift}
-let directoryURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).URLByAppendingPathComponent(NSProcessInfo.processInfo().globallyUniqueString, isDirectory: true)
-do {
- try NSFileManager.defaultManager().createDirectoryAtURL(directoryURL, withIntermediateDirectories: true, attributes: nil)
-} catch {
- // ...
-}
-~~~
-~~~{objective-c}
-NSURL *directoryURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]] isDirectory:YES];
-[[NSFileManager defaultManager] createDirectoryAtURL:directoryURL withIntermediateDirectories:YES attributes:nil error:&error];
-~~~
-
-And, of course, temporary file paths relative to this directory can be created with `URLByAppendingPathComponent:`:
-
-~~~{swift}
-let fileURL = directoryURL.URLByAppendingPathComponent(fileName)
-~~~
-~~~{objective-c}
-NSURL *fileURL = [directoryURL URLByAppendingPathComponent:fileName];
-~~~
-
-## Writing to a Temporary File
-
-Files don't exist on the file system until a particular file path is either touched or written to.
-
-### NSData -writeToURL:options:error
-
-There are several ways in which data is written to disk in Foundation. The most straightforward of which is `NSData -writeToURL:options:error`:
-
-~~~{swift}
-let data: NSData = ...
-do {
- try data.writeToURL(fileURL, options: .AtomicWrite)
-} catch {
- // ...
-}
-~~~
-~~~{objective-c}
-NSData *data = ...;
-NSError *error = nil;
-[data writeToURL:fileURL options:NSDataWritingAtomic error:&error];
-~~~
-
-### NSOutputStream
-
-For more advanced APIs, it is not uncommon to pass an `NSOutputStream` instance to direct the flow of data. Again, creating an output stream to a temporary file path is no different than any other kind of file path:
-
-~~~{swift}
-let outputStream = NSOutputStream(toFileAtPath: fileURL.absoluteString, append: false)
-~~~
-~~~{objective-c}
-NSOutputStream *outputStream = [NSOutputStream outputStreamToFileAtPath:[fileURL absoluteString] append:NO];
-~~~
-
-### Cleaning Up
-
-The final step is what makes a temporary file _actually temporary_: clean up.
-
-Although files in a system-designated temporary directory make no guarantees about how long they'll exist before being deleted automatically by the operating system (up to a few days, according to scattered reports), it's still good practice to take care of it yourself once you're finished.
-
-Do that with `NSFileManager -removeItemAtURL:`, which works for both a temporary file and a temporary directory:
-
-~~~{swift}
-do {
- try NSFileManager.defaultManager().removeItemAtURL(fileURL)
-} catch {
- // ...
-}
-~~~
-~~~{objective-c}
-NSError *error = nil;
-[[NSFileManager defaultManager] removeItemAtURL:fileURL error:&error];
-~~~
-
-* * *
-
-"This too shall pass" is a mantra that acknowledges that all things are indeed temporary. Within the context of the application lifecycle, some things are more temporary than others, and it is in that knowledge that we 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.
diff --git a/2014-03-03-temporary-files.md b/2014-03-03-temporary-files.md
new file mode 100644
index 00000000..e82eba37
--- /dev/null
+++ b/2014-03-03-temporary-files.md
@@ -0,0 +1,383 @@
+---
+title: "Temporary Files"
+author: Mattt
+category: Cocoa
+excerpt: >-
+ 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).
+revisions:
+ "2014-03-03": Original publication
+ "2018-10-24": Updated for Swift 4.2
+ "2018-11-21": Corrected use of `url(for:in:appropriateFor:create:)`
+ "2018-11-21": Corrected use of `url(for:in:appropriateFor:create:)`
+ "2019-03-09": Corrected use of deprecated `NSData.WritingOptions.atomicWrite`
+status:
+ swift: 4.2
+---
+
+Volumes have been written about persisting data,
+but when it comes to short-lived, temporary files,
+there is very little to go on for Cocoa.
+(Or if there has, perhaps it was poetically ephemeral itself).
+
+---
+
+Temporary files are used to write data to disk
+before either moving it to a permanent location
+or discarding it.
+For example, when a movie editor app exports a project,
+it may write each frame to a temporary file until it reaches the end
+and moves the completed file to the `~/Movies` directory.
+Using a temporary file for these kinds of situations
+ensures that tasks are completed atomically
+(either you get a finished product or nothing at all; nothing half-way),
+and without creating excessive memory pressure on the system
+(on most computers, disk space is plentiful whereas memory is limited).
+
+There are four distinct steps to working with a temporary file:
+
+1. Creating a temporary directory in the filesystem
+2. Creating a temporary file in that directory with a unique filename
+3. Writing data to the temporary file
+4. Moving or deleting the temporary file once you're finished with it
+
+## Creating a Temporary Directory
+
+The first step to creating a temporary file
+is to find a reasonable, out-of-the-way location to which you can write ---
+somewhere inconspicuous that doesn't
+get in the way of the user
+or get picked up by a system process like
+Spotlight indexing, Time Machine backups, or iCloud sync.
+
+On Unix systems, the `/tmp` directory is the de facto scratch space.
+However, today's macOS and iOS apps run in a container
+and don't have access to system directories;
+a hard-coded path like that isn't going to cut it.
+
+If you don't intend to keep the temporary file around,
+you can use the `NSTemporaryDirectory()` function
+to get a path to a temporary directory for the current user.
+
+```swift
+let temporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory(),
+ isDirectory: true)
+```
+
+```objc
+NSURL *temporaryDirectoryURL = [NSURL fileURLWithPath: NSTemporaryDirectory()
+ isDirectory: YES];
+```
+
+Alternatively,
+if you intend to move your temporary file to a destination URL,
+the preferred (albeit more complicated) approach
+is to call the `FileManager` method `uri(for:in:appropriateFor:create:)`.
+
+```swift
+let destinationURL: URL = <#/path/to/destination#>
+let temporaryDirectoryURL =
+ try FileManager.default.url(for: .itemReplacementDirectory,
+ in: .userDomainMask,
+ appropriateFor: destinationURL,
+ create: true)
+```
+
+```objc
+NSURL *destinationURL = <#/path/to/destination#>;
+
+NSFileManager *fileManager = [NSFileManager defaultManager];
+NSError *error = nil;
+NSURL *temporaryDirectoryURL =
+ [fileManager URLForDirectory:NSItemReplacementDirectory
+ inDomain:NSUserDomainMask
+ appropriateForURL:destinationURL
+ create:YES
+ error:&error];
+```
+
+The parameters of this method are frequently misunderstood,
+so let's go through each to understand what this method actually does:
+
+- We pass the item replacement search path (`.itemReplacementDirectory`)
+ to say that we're interested in a temporary directory.
+- We pass the user domain mask (`.userDomainMask`)
+ to get a directory that's accessible to the user.
+- For the `appropriateForURL` parameter,
+ we specify our `destinationURL`,
+ so that the system returns a temporary directory
+ from which a file can be quickly moved to the destination
+ (and not, say across different volumes).
+- Finally, we pass `true` to the `create` parameter
+ to save us the additional step of creating it ourselves.
+
+The resulting directory will have a path that looks something like this:
+file:///var/folders/l3/kyksr35977d8nfl1mhw6l_c00000gn/T/TemporaryItems/(A%20Document%20Being%20Saved%20By%20NSHipster%208)/
+
+## Creating a Temporary File
+
+With a place to call home (at least temporarily),
+the next step is to figure out what to call our temporary file.
+We're not picky about what 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`:
+
+```swift
+ProcessInfo().globallyUniqueString
+```
+
+```objc
+[[NSProcessInfo processInfo] globallyUniqueString];
+```
+
+The resulting filename will look something like this:
+42BC63F7-E79E-4E41-8E0D-B72B049E9254-25121-000144AB9F08C9C1
+
+Alternatively,
+[`UUID`](https://nshipster.com/uuid-udid-unique-identifier)
+also produces workably unique identifiers:
+
+```swift
+UUID().uuidString
+```
+
+```objc
+[[NSUUID UUID] UUIDString]
+```
+
+A generated UUID string has the following format:
+B49C292E-573D-4F5B-A362-3F2291A786E7
+
+Now that we have an appropriate directory and a unique filename,
+let's put them together to create our temporary file:
+
+```swift
+let destinationURL: URL = <#/path/to/destination#>
+
+let temporaryDirectoryURL =
+ try FileManager.default.url(for: .itemReplacementDirectory,
+ in: .userDomainMask,
+ appropriateFor: destinationURL,
+ create: true)
+
+let temporaryFilename = ProcessInfo().globallyUniqueString
+
+let temporaryFileURL =
+ temporaryDirectoryURL.appendingPathComponent(temporaryFilename)
+```
+
+```objc
+NSURL *destinationURL = <#/path/to/destination#>;
+
+NSFileManager *fileManager = [NSFileManager defaultManager];
+NSError *error = nil;
+NSURL *temporaryDirectoryURL =
+ [fileManager URLForDirectory:NSItemReplacementDirectory
+ inDomain:NSUserDomainMask
+ appropriateForURL:destinationURL
+ create:YES
+ error:&error];
+
+NSString *temporaryFilename =
+ [[NSProcessInfo processInfo] globallyUniqueString];
+NSURL *temporaryFileURL =
+ [temporaryDirectoryURL
+ URLByAppendingPathComponent:temporaryFilename];
+```
+
+## Writing to a Temporary File
+
+The sole act of creating a file URL is of no consequence to the file system;
+a file is created only when the file path is written to.
+So let's talk about our options for doing that:
+
+### Writing Data to a URL
+
+The simplest way to write data to a file
+is to call the `Data` method `write(to:options)`:
+
+```swift
+let data: Data = <#some data#>
+try data.write(to: temporaryFileURL,
+ options: .atomic)
+```
+
+```objc
+NSData *data = <#some data#>;
+NSError *error = nil;
+[data writeToURL:temporaryFileURL
+ options:NSDataWritingAtomic
+ error:&error];
+```
+
+By passing the `atomic` option,
+we ensure that either all of the data is written
+or the method returns an error.
+
+### Writing Data to a File Handle
+
+If you're doing anything more complicated
+than writing a single `Data` object to a file,
+you might instead create an empty file
+and use a `FileHandle` to write data incrementally.
+
+```swift
+fileManager.createFile(atPath: temporaryFileURL.path, contents: Data())
+
+let fileHandle = try FileHandle(forWritingTo: temporaryFileURL)
+defer { fileHandle.closeFile() }
+
+fileHandle.write(data)
+
+// ...
+```
+
+```objc
+[fileManager createFileAtPath: [temporaryFileURL path]
+ contents: [NSData data]
+ attributes: @{}];
+
+NSError *error = nil;
+NSFileHandle *fileHandle =
+ [NSFileHandle fileHandleForWritingToURL:temporaryFileURL
+ error:&error];
+[fileHandle writeData:data];
+
+// ...
+
+[fileHandle closeFile];
+```
+
+### Writing Data to an Output Stream
+
+For more advanced APIs,
+it's not uncommon to use `OutputStream`
+to direct the flow of data.
+Creating an output stream to a temporary file
+is no different than any other kind of file:
+
+```swift
+let outputStream =
+ OutputStream(url: temporaryFileURL, append: true)!
+defer { outputStream.close() }
+
+data.withUnsafeBytes { bytes in
+ outputStream.write(bytes, maxLength: bytes.count)
+}
+```
+
+```objc
+NSOutputStream *outputStream =
+ [NSOutputStream outputStreamWithURL:temporaryFileURL
+ append:YES];
+
+[outputStream write:data.bytes
+ maxLength:data.length];
+
+[outputStream close];
+```
+
+{% info %}
+In Swift,
+calling `fileHandle.closeFile()` or
+`outputStream.close()`
+within a [`defer`](https://nshipster.com/guard-and-defer/) statement
+is a convenient way to fulfill the API contract
+of closing a file when we're done with it.
+(Of course, don't do this if you want to keep the file handle open
+longer than the enclosing scope).
+{% endinfo %}
+
+## 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:
+
+```swift
+let fileURL: URL = <#/path/to/file#>
+try FileManager.default.moveItem(at: temporaryFileURL,
+ to: fileURL)
+```
+
+```objc
+NSFileManager *fileManager = [NSFileManager defaultManager];
+
+NSURL *fileURL = <#/path/to/file#>;
+NSError *error = nil;
+[fileManager moveItemAtURL:temporaryFileURL
+ toURL:fileURL
+ error:&error];
+```
+
+{% info %}
+
+Or, if you're not entirely settled on that,
+you can use the same approach
+to locate a cache directory where the file can lie low for a while:
+
+```swift
+let cacheDirectoryURL =
+ try FileManager.default.url(for: .cachesDirectory,
+ in: .userDomainMask,
+ appropriateFor: nil,
+ create: false)
+```
+
+```objc
+NSFileManager *fileManager = [NSFileManager defaultManager];
+
+NSError *error = nil;
+NSURL *cacheDirectoryURL =
+ [fileManager URLForDirectory:NSCachesDirectory
+ inDomain:NSUserDomainMask
+ appropriateForURL:nil
+ create:NO
+ error:&error];
+```
+
+{% endinfo %}
+
+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:
+
+```swift
+try FileManager.default.removeItem(at: temporaryFileURL)
+```
+
+```objc
+NSFileManager *fileManager = [NSFileManager defaultManager];
+
+NSError *error = nil;
+[fileManager removeItemAtURL:temporaryFileURL
+ error:&error];
+```
+
+---
+
+_"This too shall pass"_
+is a mantra that acknowledges that all things are indeed temporary.
+
+Within the context of the application lifecycle,
+some things are more temporary than others,
+and it's with that knowledge that we choose to act appropriately:
+seeking to find the right place,
+make a unique impact,
+and leave without a trace.
+
+Perhaps we can learn something from this cycle in our own,
+brief and glorious lifecycle.
diff --git a/2014-03-10-dictionary-services.md b/2014-03-10-dictionary-services.md
index ad4ebebf..3e880256 100644
--- a/2014-03-10-dictionary-services.md
+++ b/2014-03-10-dictionary-services.md
@@ -1,28 +1,52 @@
---
-title: "UIReferenceLibraryViewController / DCSDictionaryRef/ /usr/share/dict/words"
-author: Mattt Thompson
+title: Dictionary Services
+author: Mattt
category: Cocoa
tags: cfhipsterref
-excerpt: "Though widely usurped of their 'go-to reference' status by the Internet, dictionaries and word lists serve an important role behind the scenes of functionality ranging from spell check, grammar check, and auto-correct to auto-summarization and semantic analysis."
+excerpt: >-
+ Though widely usurped of their 'go-to reference' status by the Internet,
+ dictionaries and word lists serve an important role
+ behind the scenes for features ranging from
+ spell check, grammar check, and auto-correct to
+ auto-summarization and semantic analysis.
+revisions:
+ "2014-03-10": Original publication
+ "2019-01-07": Updated for Swift 4.2
status:
- swift: t.b.c.
+ swift: 4.2
---
-
-
-This week's article is about dictionaries. Not the `NSDictionary` / `CFDictionaryRef` we encounter everyday, but those distant lexicographic vestiges of school days past.
-
-> But seriously, why are dictionaries called that, anyway? Why can't we just settle on `Hash`, like those nice Ruby folks? What's that? Semantic overlap with hashing functions and cryptographic digests? Well, dictionary isn't _that_ bad. Anything other than "associative arrays", I reckon.
-
-Though widely usurped of their "go-to reference" status by the Internet, dictionaries and word lists serve an important role behind the scenes of functionality ranging from spell check, grammar check, and auto-correct to auto-summarization and semantic analysis. So, for your reference, here's a look at the ways and means by which computers give meaning to the world through words, in Unix, OS X, and iOS.
-
-* * *
+This week's article is about dictionaries.
+No, not the `Dictionary` / `NSDictionary` / `CFDictionaryRef`
+we encounter every day,
+but rather those distant lexicographic vestiges of school days past.
+
+{% info %}
+But seriously, why are dictionaries called that, anyway?
+Why can't we just settle on `Hash`, like those nice Ruby folks?
+_What's that?
+Semantic overlap with hashing functions and cryptographic digests?_
+Well, dictionary isn't _that_ bad.
+Anything's better than "associative arrays", I suppose...
+{% endinfo %}
+
+Though widely usurped of their 'go-to reference' status by the Internet,
+dictionaries and word lists serve an important role
+behind the scenes for features ranging from
+spell check, grammar check, and auto-correct to
+auto-summarization and semantic analysis.
+So, for your reference,
+here's a look at the ways and means by which
+computers give meaning to the world through words,
+in Unix, macOS, and iOS.
## Unix
-Nearly all Unix distributions include a small collection newline-delimited list of words. On OS X, these can be found at `/usr/share/dict`:
+Nearly all Unix distributions include
+a small collection newline-delimited list of words.
+On macOS, these can be found at `/usr/share/dict`:
-~~~bash
+```terminal
$ ls /usr/share/dict
README
connectives
@@ -30,18 +54,22 @@ $ ls /usr/share/dict
web2
web2a
words@ -> web2
-~~~
+```
-Symlinked to `words` is the `web2` word list, which, though not exhaustive, is still a sizable corpus:
+Symlinked to `words` is the `web2` word list,
+which ---
+though not exhaustive ---
+is still a sizable corpus:
-~~~bash
+```terminal
$ wc /usr/share/dict/words
235886 235886 2493109
-~~~
+```
-Skimming with `head` shows what fun lies herein. Such excitement is rarely so palpable as it is among words beginning with "a":
+Skimming with `head` shows what fun lies herein.
+Such excitement is rarely so palpable as it is among words beginning with "a":
-~~~bash
+```terminal
$ head /usr/share/dict/words
A
a
@@ -53,22 +81,32 @@ $ head /usr/share/dict/words
aardvark
aardwolf
Aaron
-~~~
-
-These giant, system-provided text files make it easy to `grep` crossword puzzle clues, generate mnemonic pass phrases, and seed databases, but from a user perspective, `/usr/share/dict`'s monolingualism and lack of associated meaning make it less than useful for everyday use.
-
-OS X builds upon this with its own system dictionaries. Never one to disappoint, the operating system's penchant for extending Unix functionality through strategically placed bundles and plist files is in full force with how dictionaries are distributed.
-
-* * *
-
-## OS X
-
-The OS X analog to `/usr/share/dict` can be found in `/Library/Dictionaries`.
-A quick peek into the shared system dictionaries demonstrates one immediate improvement over Unix, by acknowledging the existence of languages other than English:
-
-~~~bash
+```
+
+These giant, system-provided text files make it easy to
+`grep` crossword puzzle clues,
+generate mnemonic passphrases, and
+seed databases.
+But from a user perspective,
+`/usr/share/dict`'s monolingualism
+and lack of associated meaning
+make it less than helpful for everyday use.
+
+macOS builds upon this with its own system dictionaries.
+Never one to disappoint,
+the operating system's penchant for extending Unix functionality
+by way of strategically placed bundles and plist files
+is in full force here with how dictionaries are distributed.
+
+## macOS
+
+The macOS analog to `/usr/share/dict` can be found in `/Library/Dictionaries`.
+A quick peek into the shared system dictionaries
+demonstrates one immediate improvement over Unix:
+acknowledging the existence of languages other than English:
+
+```terminal
$ ls /Library/Dictionaries/
-
Apple Dictionary.dictionary/
Diccionario General de la Lengua Española Vox.dictionary/
Duden Dictionary Data Set I.dictionary/
@@ -85,15 +123,18 @@ $ ls /Library/Dictionaries/
Sanseido The WISDOM English-Japanese Japanese-English Dictionary.dictionary/
Simplified Chinese - English.dictionary/
The Standard Dictionary of Contemporary Chinese.dictionary/
-~~~
+```
-OS X ships with dictionaries in Chinese, English, French, Dutch, Italian, Japanese, Korean, as well as an English thesaurus and a special dictionary for Apple-specific terminology.
+macOS ships with dictionaries in
+Chinese, English, French, Dutch, Italian, Japanese, and Korean,
+as well as an English thesaurus
+and a special dictionary for Apple-specific terminology.
-Diving deeper into the rabbit hole, we peruse the `.dictionary` bundles to see them for what they really are:
+Diving deeper into the rabbit hole,
+we peruse the `.dictionary` bundles to see them for what they really are:
-~~~bash
+```terminal
$ ls "/Library/Dictionaries/New Oxford American Dictionary.dictionary/Contents"
-
Body.data
DefaultStyle.css
EntryID.data
@@ -105,43 +146,86 @@ $ ls "/Library/Dictionaries/New Oxford American Dictionary.dictionary/Contents"
Resources/
_CodeSignature/
version.plist
-~~~
+```
-A filesystem autopsy reveals some interesting implementation details. In the case of the New Oxford American Dictionary in particular, contents include:
+{% comment %}
+🧛🏼
+(What is a dictionary?
+A miserable little pile of definitions.
+But enough talk...
+Have at you!)
+{% endcomment %}
-- Binary-encoded `KeyText.data`, `KeyText.index`, & `Content.data`
+A filesystem autopsy reveals some interesting implementation details.
+For New Oxford American Dictionary, in particular,
+its contents include:
+
+- Binary-encoded `KeyText.data`, `KeyText.index`, and `Content.data`
- CSS for styling entries
- 1207 images, from A-Frame to Zither.
-- Preference to switch between [US English Diacritical Pronunciation](http://en.wikipedia.org/wiki/Pronunciation_respelling_for_English) and [IPA](http://en.wikipedia.org/wiki/International_Phonetic_Alphabet) (International Phonetic Alphabet)
-- Manifest & signature for dictionary contents
+- Preference to switch between
+ [US English Diacritical Pronunciation](https://en.wikipedia.org/wiki/Pronunciation_respelling_for_English) and
+ [International Phonetic Alphabet (IPA)](https://en.wikipedia.org/wiki/International_Phonetic_Alphabet)
+
+Proprietary binary encoding like this would usually be
+the end of the road in terms of what one could reasonably do with data.
+Luckily, Core Services provides APIs to read this information.
+
+### Getting the Definition of Words
+
+To get the definition of a word on macOS,
+one can use the `DCSCopyTextDefinition` function
+found in the Core Services framework:
+
+```swift
+import Foundation
+import CoreServices.DictionaryServices
+
+func define(_ word: String) -> String? {
+ let nsstring = word as NSString
+ let cfrange = CFRange(location: 0, length: nsstring.length)
-Normally, proprietary binary encoding would be the end of the road in terms of what one could reasonably do with data, but luckily, Core Services provides APIs to read this information.
+ guard let definition = DCSCopyTextDefinition(nil, nsstring, cfrange) else {
+ return nil
+ }
-#### Getting Definition of Word
+ return String(definition.takeUnretainedValue())
+}
-To get the definition of a word on OS X, one can use the `DCSCopyTextDefinition` function, found in the Core Services framework:
+define("apple") // "apple | ˈapəl | noun 1 the round fruit of a tree..."
+```
-~~~{objective-c}
+```objc
#import
NSString *word = @"apple";
NSString *definition = (__bridge_transfer NSString *)DCSCopyTextDefinition(NULL, (__bridge CFStringRef)word, CFRangeMake(0, [word length]));
NSLog(@"%@", definition);
-~~~
+```
-Wait, where did all of those great dictionaries go?
+_Wait, where did all of those great dictionaries go?_
-Well, they all disappeared into that first `NULL` argument. One might expect to provide a `DCSCopyTextDefinition` type here, as prescribed by the function definition. However, there are no public functions to construct or copy such a type, making `NULL` the only available option. The documentation is as clear as it is stern:
+Well, they all disappeared into that first `NULL` argument.
+One might expect to provide a `DCSCopyTextDefinition` type here ---
+as prescribed by the function definition.
+However, there are no public functions to construct or copy such a type,
+making `nil` the only available option.
+The documentation is as clear as it is stern:
-> This parameter is reserved for future use, so pass `NULL`. Dictionary Services searches in all active dictionaries.
+> This parameter is reserved for future use, so pass `NULL`.
+> Dictionary Services searches in all active dictionaries.
-"Dictionary Services searches in **all active dictionaries**", you say? Sounds like a loophole!
+"Dictionary Services searches in **all active dictionaries**", you say?
+Sounds like a loophole!
#### Setting Active Dictionaries
-Now, there's nothing programmers love to hate to love more than the practice of exploiting loopholes to side-step Apple platform restrictions. Behold: an entirely error-prone approach to getting, say, thesaurus results instead of the first definition available in the standard dictionary:
+Now, there's nothing programmers love to hate to love more than
+exploiting loopholes to side-step Apple platform restrictions.
+Behold: an entirely error-prone approach to getting, say, thesaurus results
+instead of the first definition available in the standard dictionary:
-~~~{objective-c}
+```objc
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *dictionaryPreferences = [[userDefaults persistentDomainForName:@"com.apple.DictionaryServices"] mutableCopy];
NSArray *activeDictionaries = [dictionaryPreferences objectForKey:@"DCSActiveDictionaries"];
@@ -154,17 +238,24 @@ dictionaryPreferences[@"DCSActiveDictionaries"] = @[@"/Library/Dictionaries/Oxfo
}
dictionaryPreferences[@"DCSActiveDictionaries"] = activeDictionaries;
[userDefaults setPersistentDomain:dictionaryPreferences forName:@"com.apple.DictionaryServices"];
-~~~
+```
-"But this is OS X, a platform whose manifest destiny cannot be contained by meager sandboxing attempts from Cupertino!", you cry. "Isn't there a more civilized approach? Like, say, private APIs?"
+_"But this is macOS,
+a platform whose manifest destiny
+cannot be contained by meager sandboxing attempts from Cupertino!"_,
+you cry.
+_"Isn't there a more civilized approach? Like, say, private APIs?"_
-Why yes, yes there are.
+Why yes.
+Yes there are.
-### Private APIs
+#### Exploring Private APIs
-Not publicly exposed, but still available through Core Services are a number of functions that cut closer to the dictionary services functionality that we crave:
+Not publicly exposed but still available through Core Services
+are a number of functions that cut closer to
+the dictionary services functionality we crave:
-~~~{objective-c}
+```objc
extern CFArrayRef DCSCopyAvailableDictionaries();
extern CFStringRef DCSDictionaryGetName(DCSDictionaryRef dictionary);
extern CFStringRef DCSDictionaryGetShortName(DCSDictionaryRef dictionary);
@@ -182,13 +273,15 @@ extern CFStringRef DCSRecordGetRawHeadword(CFTypeRef record);
extern CFStringRef DCSRecordGetString(CFTypeRef record);
extern CFStringRef DCSRecordGetTitle(CFTypeRef record);
extern DCSDictionaryRef DCSRecordGetSubDictionary(CFTypeRef record);
-~~~
+```
-Private as they are, these functions aren't about to start documenting themselves, so let's take a look at how they're used:
+Private as they are,
+these functions aren't about to start documenting themselves.
+So let's take a look at how they're used:
#### Getting Available Dictionaries
-~~~{objective-c}
+```objc
NSMapTable *availableDictionariesKeyedByName =
[NSMapTable mapTableWithKeyOptions:NSPointerFunctionsCopyIn
valueOptions:NSPointerFunctionsObjectPointerPersonality];
@@ -197,13 +290,15 @@ for (id dictionary in (__bridge_transfer NSArray *)DCSCopyAvailableDictionaries(
NSString *name = (__bridge NSString *)DCSDictionaryGetName((__bridge DCSDictionaryRef)dictionary);
[availableDictionariesKeyedByName setObject:dictionary forKey:name];
}
-~~~
+```
#### Getting Definition for Word
-With instances of the elusive `DCSDictionaryRef` type available at our disposal, we can now see what all of the fuss is about with that first argument in `DCSCopyTextDefinition`:
+With instances of the elusive `DCSDictionaryRef` type available at our disposal,
+we can now see what all of the fuss is about with
+that first argument in `DCSCopyTextDefinition`:
-~~~{objective-c}
+```objc
NSString *word = @"apple";
for (NSString *name in availableDictionariesKeyedByName) {
@@ -230,50 +325,74 @@ for (NSString *name in availableDictionariesKeyedByName) {
}
}
}
-~~~
-
-Most surprising from this experimentation is the ability to access the raw HTML for entries, which combined with a dictionary's bundled CSS, produces the result seen in Dictionary.app.
+```
-
+Most surprising from this experimentation
+is the ability to access the raw HTML for entries,
+which --- combined with a dictionary's bundled CSS ---
+produces the result seen in Dictionary.app.
-> For any fellow linguistics nerds or markup curious folks out there, here's [the HTML of the entry for the word "apple"](https://gist.github.com/mattt/9453538).
+{% asset dictionary.png alt="Entry for "apple" in Dictionary.app" %}
-In the process of writing this article, I _accidentally_ created [an Objective-C wrapper](https://github.com/mattt/DictionaryKit) around this forbidden fruit (so forbidden by our favorite fruit company, so don't try submitting this to the App Store).
-
-* * *
+{% info %}
+In the process of writing this article,
+I _accidentally_ created a
+[wrapper library](https://github.com/mattt/DictionaryKit)
+around this forbidden fruit
+(that is, forbidden by our favorite fruit company;
+don't try submitting this to the App Store).
+{% endinfo %}
## iOS
-iOS development is a decidedly more by-the-books affair, so attempting to reverse-engineer the platform would be little more than an academic exercise. Fortunately, a good chunk of functionality is available (as of iOS 5) through the obscure UIKit class `UIReferenceLibraryViewController`.
+iOS development is a decidedly more by-the-books affair,
+so attempting to reverse-engineer the platform
+would be little more than an academic exercise.
+Fortunately, a good chunk of functionality is available
+through an obscure UIKit class, `UIReferenceLibraryViewController`.
-`UIReferenceLibraryViewController` is similar to an `MFMessageComposeViewController`, in that provides a minimally-configurable view controller around system functionality, intended to be presented modally.
+`UIReferenceLibraryViewController` is similar to an
+`MFMessageComposeViewController` in that provides
+a minimally-configurable view controller around system functionality
+that's intended to present modally.
-Simply initialize with the desired term:
+You initialize it with the desired term:
-~~~{objective-c}
+```objc
UIReferenceLibraryViewController *referenceLibraryViewController =
[[UIReferenceLibraryViewController alloc] initWithTerm:@"apple"];
[viewController presentViewController:referenceLibraryViewController
animated:YES
completion:nil];
-~~~
-
-
+```
-This is the same behavior that one might encounter by tapping the "Define" `UIMenuItem` on a highlighted word in a `UITextView`.
+{% asset uireferencelibraryviewcontroller-1.png alt="Presenting a UIReferenceLibraryViewController" %}
-> Tapping on "Manage" brings up a view to download additional dictionaries.
+This is the same behavior that one might encounter
+when tapping the "Define" menu item on a highlighted word in a text view.
-
+{% asset uireferencelibraryviewcontroller-2.png alt="Presenting a UIReferenceLibraryViewController" %}
-`UIReferenceLibraryViewController` also provides the class method `dictionaryHasDefinitionForTerm:`. A developer would do well to call this before presenting a dictionary view controller that will inevitably have nothing to display.
+`UIReferenceLibraryViewController` also provides
+the class method `dictionaryHasDefinitionForTerm:`.
+A developer would do well to call this
+before presenting a dictionary view controller
+that will inevitably have nothing to display.
-~~~{objective-c}
+```objc
[UIReferenceLibraryViewController dictionaryHasDefinitionForTerm:@"apple"];
-~~~
+```
-> In both cases, it appears that `UIReferenceLibraryViewController` will do its best to normalize the search term, so stripping whitespace or changing to lowercase should not be necessary.
+{% info %}
+In both cases, it appears that `UIReferenceLibraryViewController` will do its best to normalize the search term, so stripping whitespace or changing to lowercase should not be necessary.
+{% endinfo %}
-* * *
+---
-From Unix word lists to their evolved `.dictionary` bundles on OS X (and presumably iOS), words are as essential to application programming as mathematical constants and the "Sosumi" alert noise. Consider how the aforementioned APIs can be integrated into your own app, or used to create a kind of app you hadn't previously considered. There are a [wealth](http://nshipster.com/nslocalizedstring/) [of](http://nshipster.com/nslinguistictagger/) [linguistic](http://nshipster.com/search-kit/) [technologies](http://nshipster.com/uilocalizedindexedcollation/) baked into Apple's platforms, so take advantage of them.
+From Unix word lists to their evolved `.dictionary` bundles on macOS
+(and presumably iOS, too),
+words are as essential to application programming
+as mathematical constants and the
+["Sosumi"](https://en.wikipedia.org/wiki/Sosumi) alert noise.
+Consider how the aforementioned APIs can be integrated into your own app,
+or used to create a kind of app you hadn't previously considered.
diff --git a/2014-03-17-empathy.md b/2014-03-17-empathy.md
index fc26c133..bfcfa38c 100644
--- a/2014-03-17-empathy.md
+++ b/2014-03-17-empathy.md
@@ -1,7 +1,7 @@
---
title: Empathy
-author: Mattt Thompson
-category: ""
+author: Mattt
+category: Miscellaneous
tags: nshipster
excerpt: "We naturally want to help one another, to explain ideas, to be generous and patient. However, on the Internet, human nature seems to drop a few packets."
status:
diff --git a/2014-03-24-nsurl.md b/2014-03-24-nsurl.md
index 810dc765..71955acf 100644
--- a/2014-03-24-nsurl.md
+++ b/2014-03-24-nsurl.md
@@ -1,35 +1,35 @@
---
title: "NSURL / NSURLComponents"
-author: Mattt Thompson
+author: Mattt
category: Cocoa
excerpt: "Of all the one-dimensional data types out there, URIs reign supreme. Here, in a single, human-parsable string, is every conceivable piece of information necessary to encode the location of any piece of information that has, does, and will ever exist on a computer."
revisions:
- "2014-03-24": Original publication.
- "2015-09-08": Added Swift translation & section on `stringByAddingPercentEncodingWithAllowedCharacters`.
+ "2014-03-24": Original publication
+ "2015-09-08": Added Swift translation & section on `stringByAddingPercentEncodingWithAllowedCharacters`
status:
- swift: 2.0
- reviewed: September 9, 2015
+ swift: 2.0
+ reviewed: September 9, 2015
---
-There is a simple beauty to—let's call them "one-dimensional data types": numbers or strings formatted to contain multiple values, retrievable through a mathematical operator or parsing routine. Like how the hexadecimal color #EE8262 can have [red, green, and blue components](http://en.wikipedia.org/wiki/Web_colors) extracted by masking and shifting its bits, or how [regular expressions](http://en.wikipedia.org/wiki/Regular_expression) can match and capture complex patterns in just a few characters.
+There is a simple beauty to—let's call them "one-dimensional data types": numbers or strings formatted to contain multiple values, retrievable through a mathematical operator or parsing routine. Like how the hexadecimal color #EE8262 can have [red, green, and blue components](https://en.wikipedia.org/wiki/Web_colors) extracted by masking and shifting its bits, or how [regular expressions](https://en.wikipedia.org/wiki/Regular_expression) can match and capture complex patterns in just a few characters.
-Of all the one-dimensional data types out there, [URIs](http://en.wikipedia.org/wiki/URI_scheme) reign supreme. Here, in a single, human-parsable string, is every conceivable piece of information necessary to encode the location of any piece of information that has, does, and will ever exist on a computer.
+Of all the one-dimensional data types out there, [URIs](https://en.wikipedia.org/wiki/URI_scheme) reign supreme. Here, in a single, human-parsable string, is every conceivable piece of information necessary to encode the location of any piece of information that has, does, and will ever exist on a computer.
In its most basic form, a URI is comprised of a scheme name and a hierarchical part, with an optional query and fragment:
-~~~
+```
: [ ? ] [ # ]
-~~~
+```
Many protocols, including HTTP, specify a regular structure for information like the username, password, port, and path in the hierarchical part:
-
+
A solid grasp of network programming is rooted in an unshakeable familiarity with URL components. As a software developer, this means having a command over the URI functionality in your programming language's standard library.
> If a programming language does not have a URI module in its standard library, run, don't walk, to a real language that does.
-* * *
+---
In Foundation, URLs are represented by `NSURL`.
@@ -38,13 +38,14 @@ In Foundation, URLs are represented by `NSURL`.
```swift
let url = NSURL(string: "http://example.com/")
```
-```objective-c
+
+```objc
NSURL *url = [NSURL URLWithString:@"http://example.com"];
```
If the passed string is not a valid URL, this initializer will return `nil`.
-`NSString` also has some vestigial functionality for path manipulation, [as described a few weeks back](http://nshipster.com/nstemporarydirectory/), but that's being slowly migrated over to `NSURL`. While the extra conversion from `NSString` to `NSURL` is not the most convenient step, it's always worthwhile. If a value is a URL, it should be stored and passed as an `NSURL`; conflating the two types is reckless and lazy API design.
+`NSString` also has some vestigial functionality for path manipulation, [as described a few weeks back](https://nshipster.com/nstemporarydirectory/), but that's being slowly migrated over to `NSURL`. While the extra conversion from `NSString` to `NSURL` is not the most convenient step, it's always worthwhile. If a value is a URL, it should be stored and passed as an `NSURL`; conflating the two types is reckless and lazy API design.
`NSURL` also has the initializer `NSURL(string:relativeToURL:)`, which can be used to construct a URL from a string relative to a base URL. The behavior of this method can be a source of confusion, because of how it treats leading `/`'s in relative paths.
@@ -71,7 +72,8 @@ NSURL(string:"/foo/", relativeToURL: baseURL)
NSURL(string:"http://example2.com/", relativeToURL: baseURL)
// http://example2.com/
```
-```objective-c
+
+```objc
NSURL *baseURL = [NSURL URLWithString:@"http://example.com/v1/"];
[NSURL URLWithString:@"foo" relativeToURL:baseURL];
@@ -126,7 +128,7 @@ The documentation and examples found in [`NSURL`'s documentation](https://develo
Quietly added in iOS 7 and OS X Mavericks was `NSURLComponents`, which can best be described by what it could have been named instead: `NSMutableURL`.
-`NSURLComponents` instances are constructed much in the same way as `NSURL`, with a provided `NSString` and optional base URL to resolve against (`NSURLComponents(string:)` & `NSURLComponents(URL:resolvingAgainstBaseURL:)`). It can also be initialized without any arguments to create an empty storage container, similar to [`NSDateComponents`](http://nshipster.com/nsdatecomponents/).
+`NSURLComponents` instances are constructed much in the same way as `NSURL`, with a provided `NSString` and optional base URL to resolve against (`NSURLComponents(string:)` & `NSURLComponents(URL:resolvingAgainstBaseURL:)`). It can also be initialized without any arguments to create an empty storage container, similar to [`NSDateComponents`](https://nshipster.com/nsdatecomponents/).
The difference here, between `NSURL` and `NSURLComponents`, is that component properties are read-write. This provides a safe and direct way to modify individual components of a URL:
@@ -141,7 +143,7 @@ The difference here, between `NSURL` and `NSURLComponents`, is that component pr
> Attempting to set an invalid scheme string or negative port number will throw an exception.
-In addition, `NSURLComponents` also has read-write properties for [percent-encoded](http://en.wikipedia.org/wiki/Percent-encoding) versions of each component.
+In addition, `NSURLComponents` also has read-write properties for [percent-encoded](https://en.wikipedia.org/wiki/Percent-encoding) versions of each component.
- `percentEncodedUser`
- `percentEncodedPassword`
@@ -166,7 +168,8 @@ if let escapedTitle = title.stringByAddingPercentEncodingWithAllowedCharacters(N
components.query = "title=\(escapedTitle)"
}
```
-```objective-c
+
+```objc
NSString *title = @"NSURL / NSURLComponents";
NSString *escapedTitle = [title stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
components.query = [NSString stringWithFormat:@"title=%@", escapedTitle];
@@ -185,7 +188,8 @@ extension NSCharacterSet {
public class func URLFragmentAllowedCharacterSet() -> NSCharacterSet
}
```
-```{objective-c}
+
+```objective-c
// Predefined character sets for the six URL components and subcomponents which allow percent encoding. These character sets are passed to -stringByAddingPercentEncodingWithAllowedCharacters:.
@interface NSCharacterSet (NSURLUtilities)
+ (NSCharacterSet *)URLUserAllowedCharacterSet;
@@ -197,22 +201,20 @@ extension NSCharacterSet {
@end
```
-
## Bookmark URLs
-One final topic worth mentioning are bookmark URLs, which can be used to safely reference files between application launches. Think of them as a persistent [file descriptor](http://en.wikipedia.org/wiki/File_descriptor).
+One final topic worth mentioning are bookmark URLs, which can be used to safely reference files between application launches. Think of them as a persistent [file descriptor](https://en.wikipedia.org/wiki/File_descriptor).
> A bookmark is an opaque data structure, enclosed in an `NSData` object, that describes the location of a file. Whereas path- and file reference URLs are potentially fragile between launches of your app, a bookmark can usually be used to re-create a URL to a file even in cases where the file was moved or renamed.
You can read more about bookmark URLs in ["Locating Files Using Bookmarks" from Apple's File System Programming Guide](https://developer.apple.com/library/ios/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/AccessingFilesandDirectories/AccessingFilesandDirectories.html)
-
-* * *
+---
Forget jet packs and flying cars—my idea of an exciting future is one where everything has a URL, is encoded in Markdown, and stored in Git. If your mind isn't blown by the implications of a _universal_ resource locator, then I would invite you to reconsider.
-Like [hypertext](http://en.wikipedia.org/wiki/Hypertext), universal identification is a philosophical concept that both pre-dates and transcends computers. Together, they form the fabric of our information age: a framework for encoding our collective understanding of the universe as a network of individual facts, in a fashion that is hauntingly similar to how the neurons in our brains do much the same.
+Like [hypertext](https://en.wikipedia.org/wiki/Hypertext), universal identification is a philosophical concept that both pre-dates and transcends computers. Together, they form the fabric of our information age: a framework for encoding our collective understanding of the universe as a network of individual facts, in a fashion that is hauntingly similar to how the neurons in our brains do much the same.
-We are just now crossing the precipice of a [Cambrian Explosion](http://en.wikipedia.org/wiki/Cambrian_explosion) in physical computing. In an internet of things, where [every object of our lives has a URL](http://en.wikipedia.org/wiki/IPv6#Larger_address_space) and embeds an electronic soul, a digital consciousness will emerge. Not to say, necessarily, that [the singularity is near](http://en.wikipedia.org/wiki/The_Singularity_Is_Near), but we're on the verge of something incredible.
+We are just now crossing the precipice of a [Cambrian Explosion](https://en.wikipedia.org/wiki/Cambrian_explosion) in physical computing. In an internet of things, where [every object of our lives has a URL](https://en.wikipedia.org/wiki/IPv6#Larger_address_space) and embeds an electronic soul, a digital consciousness will emerge. Not to say, necessarily, that [the singularity is near](https://en.wikipedia.org/wiki/The_Singularity_Is_Near), but we're on the verge of something incredible.
Quite lofty implications for a technology used most often to exchange pictures of cats.
diff --git a/2014-03-31-avspeechsynthesizer.md b/2014-03-31-avspeechsynthesizer.md
index 0b8e7856..4b4d7f8b 100644
--- a/2014-03-31-avspeechsynthesizer.md
+++ b/2014-03-31-avspeechsynthesizer.md
@@ -1,122 +1,229 @@
---
title: AVSpeechSynthesizer
-author: Mattt Thompson
+author: Mattt
category: Cocoa
-excerpt: "Though we're a long way off from Hal or Her, we should never forget about the billions of other people out there for us to talk to."
+excerpt: Though we're a long way off from Hal or Her,
+ we shouldn't forget about the billions of people out there for us to talk to.
+revisions:
+ "2014-03-31": Original publication
+ "2018-08-08": Updated for iOS 12 and macOS Mojave
+ "2019-12-09": Updated for iOS 13
status:
- swift: 2.0
- reviewed: September 10, 2015
+ swift: 4.2
+ reviewed: August 8, 2018
---
-Though we're a long way off from [_Hal_](https://www.youtube.com/watch?v=ARJ8cAGm6JE) or [_Her_](https://www.youtube.com/watch?v=WzV6mXIOVl4), we should never forget about the billions of other people out there for us to talk to.
+Though we're a long way off from
+[_Hal_](https://www.youtube.com/watch?v=ARJ8cAGm6JE) or
+[_Her_](https://www.youtube.com/watch?v=WzV6mXIOVl4),
+we shouldn't forget about the billions of people out there for us to talk to.
+
+Of the thousands of languages in existence,
+an individual is fortunate to gain a command of just a few within their lifetime.
+And yet,
+over several millennia of human co-existence,
+civilization has managed to make things work (more or less)
+through an ad-hoc network of
+interpreters, translators, scholars,
+and children raised in the mixed linguistic traditions of their parents.
+We've seen that mutual understanding fosters peace
+and that conversely,
+mutual unintelligibility destabilizes human relations.
+
+It's fitting that the development of computational linguistics
+should coincide with the emergence of the international community we have today.
+Working towards mutual understanding,
+intergovernmental organizations like the United Nations and European Union
+have produced a substantial corpus of
+[parallel texts](http://en.wikipedia.org/wiki/Parallel_text),
+which form the foundation of modern language translation technologies.
+
+Computer-assisted communication
+between speakers of different languages consists of three tasks:
+**transcribing** the spoken words into text,
+**translating** the text into the target language,
+and **synthesizing** speech for the translated text.
+
+This article focuses on how iOS handles the last of these: speech synthesis.
-Of the thousands of languages in existence, an individual is fortunate to gain a command of just two within their lifetime. And yet, over several millennia of human co-existence, civilization has managed to make things work, more or less, through an ad-hoc network of interpreters, translators, scholars, and children raised in the mixed linguistic traditions of their parents. We've seen that mutual understanding fosters peace, and that conversely, mutual unintelligibility destabilizes human relations.
-
-It is fitting that the development of computational linguistics should coincide with the emergence of the international community we have today. Working towards mutual understanding, intergovernmental organizations like the United Nations and European Union have produced a substantial corpora of [parallel texts](http://en.wikipedia.org/wiki/Parallel_text), which form the foundation of modern language translation technologies.
-
-> Another related linguistic development is the [Esperanto](http://en.wikipedia.org/wiki/Esperanto) language, created by L. L. Zamenhof in an effort to promote harmony between people of different countries.
-
-And while automatic text translation has reached an acceptable level for everyday communication, there is still a divide when we venture out into unfamiliar places. There is still much work to be done in order to augment our ability to communicate with one another in person.
+---
-* * *
+Introduced in iOS 7 and available in macOS 10.14 Mojave,
+`AVSpeechSynthesizer` produces speech from text.
-Introduced in iOS 7, `AVSpeechSynthesizer` produces synthesized speech from a given `AVSpeechUtterance`. Each utterance can adjust its rate of speech and pitch, and be configured to use any one of the available `AVSpeechSynthesisVoice`s:
+To use it,
+create an `AVSpeechUtterance` object with the text to be spoken
+and pass it to the `speakUtterance(_:)` method:
```swift
import AVFoundation
let string = "Hello, World!"
let utterance = AVSpeechUtterance(string: string)
-utterance.voice = AVSpeechSynthesisVoice(language: "en-US")
let synthesizer = AVSpeechSynthesizer()
synthesizer.speakUtterance(utterance)
```
-~~~{objective-c}
+```objc
NSString *string = @"Hello, World!";
AVSpeechUtterance *utterance = [[AVSpeechUtterance alloc] initWithString:string];
utterance.voice = [AVSpeechSynthesisVoice voiceWithLanguage:@"en-US"];
AVSpeechSynthesizer *synthesizer = [[AVSpeechSynthesizer alloc] init];
[synthesizer speakUtterance:utterance];
-~~~
+```
+
+You can use the adjust the volume, pitch, and rate of speech
+by configuring the corresponding properties on the `AVSpeechUtterance` object.
-When speaking, a synthesizer can either be paused immediately or on the next word boundary, which makes for a less jarring user experience.
+When speaking,
+a synthesizer can be paused on the next word boundary,
+which makes for a less jarring user experience than stopping mid-vowel.
```swift
-synthesizer.pauseSpeakingAtBoundary(.Word)
+synthesizer.pauseSpeakingAtBoundary(.word)
```
-~~~{objective-c}
+```objc
[synthesizer pauseSpeakingAtBoundary:AVSpeechBoundaryWord];
-~~~
+```
## Supported Languages
-Mac OS 9 users will no doubt have fond memories of the old system voices: Bubbles, Cellos, Pipe Organ, and Bad News.
+Mac OS 9 users will no doubt have fond memories of the old system voices ---
+especially the novelty ones, like
+Bubbles, Cellos, Pipe Organ, and Bad News.
+
+In the spirit of quality over quantity,
+each language is provided a voice for each major locale region.
+So instead of asking for "Fred" or "Markus",
+`AVSpeechSynthesisVoice` asks for `en-US` or `de-DE`.
+
+VoiceOver supports over 30 different languages.
+For an up-to-date list of what's available,
+call `AVSpeechSynthesisVoice` class method `speechVoices()`
+or check [this support article](https://support.apple.com/en-us/HT206175).
+
+By default,
+`AVSpeechSynthesizer` will speak using a voice
+based on the user's current language preferences.
+To avoid sounding like a
+[stereotypical American in Paris](https://www.youtube.com/watch?v=v-3RZl3YyJw),
+set an explicit language by selecting a `AVSpeechSynthesisVoice`.
+
+```swift
+let string = "Bonjour!"
+let utterance = AVSpeechUtterance(string: string)
+utterance.voice = AVSpeechSynthesisVoice(language: "fr")
+```
+
+```objc
+NSString *string = @"Bonjour!";
+AVSpeechUtterance *utterance = [[AVSpeechUtterance alloc] initWithString:string];
+utterance.voice = [AVSpeechSynthesisVoice voiceWithLanguage:@"fr-FR"];
+```
+
+Many APIs in foundation and other system frameworks
+use ISO 681 codes to identify languages.
+`AVSpeechSynthesisVoice`, however, takes an
+[IETF Language Tag](http://en.wikipedia.org/wiki/IETF_language_tag),
+as specified [BCP 47 Document Series](http://tools.ietf.org/html/bcp47).
+If an utterance string and voice aren't in the same language,
+speech synthesis fails.
+
+> Not all languages are preloaded on the device,
+> and may have to be downloaded in the background
+> before speech can be synthesized.
+
+{% comment %}
+
+> [This gist](https://gist.github.com/mattt/9892187)
+> shows how to detect an ISO 681 language code from an arbitrary string,
+> and convert that to an IETF language tag.
+> {% endcomment %}
+
+## Customizing Pronunciation
-> These can still be installed on OS X. Just look under the "English (United States) - Novelty" voices in the "Dictation & Speech" preference pane.
+A few years after it first debuted on iOS,
+`AVUtterance` added functionality to control
+the pronunciation of particular words,
+which is especially helpful for proper names.
-In the name of quality over quantity, each language is provided a voice for each major locale region. So instead of asking for "Fred" and "Markus", `AVSpeechSynthesisVoice` asks for `en-US` and `de-DE`.
+To take advantage of it,
+construct an utterance using `init(attributedString:)`
+instead of `init(string:)`.
+The initializer scans through the attributed string
+for any values associated with the `AVSpeechSynthesisIPANotationAttribute`,
+and adjusts pronunciation accordingly.
-As of iOS 8.1, `[AVSpeechSynthesisVoice speechVoices]` the following languages and locales are supported:
+```swift
+import AVFoundation
+
+let text = "It's pronounced 'tomato'"
+
+let mutableAttributedString = NSMutableAttributedString(string: text)
+let range = NSString(string: text).range(of: "tomato")
+let pronunciationKey = NSAttributedString.Key(rawValue: AVSpeechSynthesisIPANotationAttribute)
+
+// en-US pronunciation is /tə.ˈme͡ɪ.do͡ʊ/
+mutableAttributedString.setAttributes([pronunciationKey: "tə.ˈme͡ɪ.do͡ʊ"], range: range)
+
+let utterance = AVSpeechUtterance(attributedString: mutableAttributedString)
+
+// en-GB pronunciation is /tə.ˈmɑ.to͡ʊ/... but too bad!
+utterance.voice = AVSpeechSynthesisVoice(language: "en-GB")
+
+let synthesizer = AVSpeechSynthesizer()
+synthesizer.speak(utterance)
+```
+
+Beautiful. 🍅
-- Arabic (`ar-SA`)
-- Chinese (`zh-CN`, `zh-HK`, `zh-TW`)
-- Czech (`cs-CZ`)
-- Danish (`da-DK`)
-- Dutch (`nl-BE`, `nl-NL`)
-- English (`en-AU`, `en-GB`, `en-IE`, `en-US`, `en-ZA`)
-- Finnish (`fi-FI`)
-- French (`fr-CA`, `fr-FR`)
-- German (`de-DE`)
-- Greek (`el-GR`)
-- Hebrew (`he-IL`)
-- Hindi (`hi-IN`)
-- Hungarian (`hu-HU`)
-- Indonesian (`id-ID`)
-- Italian (`it-IT`)
-- Japanese (`ja-JP`)
-- Korean (`ko-KR`)
-- Norwegian (`no-NO`)
-- Polish (`pl-PL`)
-- Portuguese (`pt-BR`, `pt-PT`)
-- Romanian (`ro-RO`)
-- Russian (`ru-RU`)
-- Slovak (`sk-SK`)
-- Spanish (`es-ES`, `es-MX`)
-- Swedish (`sv-SE`)
-- Thai (`th-TH`)
-- Turkish (`tr-TR`)
+Of course, [this property is undocumented](https://developer.apple.com/documentation/avfoundation/avspeechsynthesisipanotationattribute)
+at the time of writing,
+so you wouldn't know that the IPA you get from Wikipedia
+won't work correctly unless you watched
+[this session from WWDC 2018](https://developer.apple.com/videos/play/wwdc2018/236/).
-`NSLocale` and `NSLinguisticTagger` both use ISO 681 codes to identify languages. `AVSpeechSynthesisVoice`, however, takes an [IETF Language Tag](http://en.wikipedia.org/wiki/IETF_language_tag), as specified [BCP 47 Document Series](http://tools.ietf.org/html/bcp47). If an utterance string and voice aren't in the same language, speech synthesis will fail.
+To get IPA notation that `AVSpeechUtterance` can understand,
+you can open the Settings app,
+navigate to Accessibility > VoiceOver > Speech > Pronunciations,
+and... say it yourself!
-> [This gist](https://gist.github.com/mattt/9892187) shows how to detect an ISO 681 language code from an arbitrary string, and convert that to an IETF language tag.
+{% asset speech-pronunciation-replacement alt="Speech Pronunciation Replacement" %}
-## Delegate Methods
+## Hooking Into Speech Events
-What makes `AVSpeechSynthesizer` really amazing for developers is the ability to hook into speech events. An object conforming to `AVSpeechSynthesizerDelegate` can be called when its speech synthesizer either starts or finishes, pauses or continues, and as each range of the utterance is spoken.
+One of the coolest features of `AVSpeechSynthesizer`
+is how it lets developers hook into speech events.
+An object conforming to `AVSpeechSynthesizerDelegate` can be called
+when a speech synthesizer
+starts or finishes,
+pauses or continues,
+and as each range of the utterance is spoken.
-For example, an app, in addition to synthesizing a voice utterance, could show that utterance in a label, and highlight the word currently being spoken:
+For example, an app ---
+in addition to synthesizing a voice utterance ---
+could show that utterance in a label,
+and highlight the word currently being spoken:
```swift
var utteranceLabel: UILabel!
// MARK: AVSpeechSynthesizerDelegate
-func speechSynthesizer(synthesizer: AVSpeechSynthesizer, willSpeakRangeOfSpeechString characterRange: NSRange, utterance: AVSpeechUtterance) {
- let mutableAttributedString = NSMutableAttributedString(string: utterance.speechString)
- mutableAttributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(), range: characterRange)
- utteranceLabel.attributedText = mutableAttributedString
-}
-
-func speechSynthesizer(synthesizer: AVSpeechSynthesizer, didFinishSpeechUtterance utterance: AVSpeechUtterance) {
- utteranceLabel.attributedText = NSAttributedString(string: utterance.speechString)
+override func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer,
+ willSpeakRangeOfSpeechString characterRange: NSRange,
+ utterance: AVSpeechUtterance)
+{
+ self.utterranceLabel.attributedText =
+ attributedString(from: utterance.speechString,
+ highlighting: characterRange)
}
```
-~~~{objective-c}
+```objc
#pragma mark - AVSpeechSynthesizerDelegate
- (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer
@@ -128,22 +235,32 @@ willSpeakRangeOfSpeechString:(NSRange)characterRange
value:[UIColor redColor] range:characterRange];
self.utteranceLabel.attributedText = mutableAttributedString;
}
+```
-- (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer
- didFinishSpeechUtterance:(AVSpeechUtterance *)utterance
-{
- self.utteranceLabel.attributedText = [[NSAttributedString alloc] initWithString:self.utteranceString];
-}
-~~~
-
-
-
-See [this example app](https://github.com/mattt/AVSpeechSynthesizer-Example) for a demonstration of live text-highlighting for all of the supported languages.
-
-* * *
+{% asset avspeechsynthesizer-example.gif alt="AVSpeechSynthesizer Example" width=320 %}
-Anyone who travels to an unfamiliar place returns with a profound understanding of what it means to communicate. It's totally different from how one is taught a language in High School: instead of genders and cases, it's about emotions and patience and clinging onto every shred of understanding. One is astounded by the extent to which two humans can communicate with hand gestures and facial expressions. One is also humbled by how frustrating it can be when pantomiming breaks down.
+Check out [this Playground](https://github.com/NSHipster/AVSpeechSynthesizer-Example)
+for an example of live text-highlighting for all of the supported languages.
-In our modern age, we have the opportunity to go out in a world augmented by a collective computational infrastructure. Armed with `AVSpeechSynthesizer` and the myriad other linguistic technologies on iOS and elsewhere, we have never been more capable of breaking down the forces that most divide our species.
+---
-If that isn't universe-denting, then I don't know what is.
+Anyone who travels to an unfamiliar place
+returns with a profound understanding of what it means to communicate.
+It's totally different from how one is taught a language in High School:
+instead of genders and cases,
+it's about emotions
+and patience
+and clinging onto every shred of understanding.
+One is astounded by the extent to which two humans
+can communicate with hand gestures and facial expressions.
+One is also humbled by how frustrating it can be when pantomiming breaks down.
+
+In our modern age, we have the opportunity to go out in a world
+augmented by a collective computational infrastructure.
+Armed with `AVSpeechSynthesizer`
+and myriad other linguistic technologies on our devices,
+we've never been more capable of breaking down the forces
+that most divide our species.
+
+If that isn't universe-denting,
+then I don't know what is.
diff --git a/2014-04-07-configuration-profiles.md b/2014-04-07-configuration-profiles.md
index 753bccec..84eaceab 100644
--- a/2014-04-07-configuration-profiles.md
+++ b/2014-04-07-configuration-profiles.md
@@ -1,7 +1,7 @@
---
title: Configuration Profiles
-author: Mattt Thompson
-category: ""
+author: Mattt
+category: Miscellaneous
excerpt: "One of the major criticisms of iOS as a platform is how locked down it is. iOS Configuration Profiles offer an interesting mechanism to work around these restrictions."
status:
swift: n/a
@@ -43,7 +43,7 @@ There are several ways to deploy configuration profiles:
> In addition to deploying configuration profiles, the [Apple Configurator](https://itunes.apple.com/us/app/apple-configurator/id434433123?mt=12) can generate profiles, as an alternative to hand-writing XML yourself.
-
+
## Use Cases
@@ -69,7 +69,7 @@ Just as EOF / WOFF / SVG fonts allow typefaces to be distributed over the web, t
### Enhancing Security
-Security has quickly become a killer feature for apps, as discussed in our article about [Multipeer Connectivity](http://nshipster.com/multipeer-connectivity/).
+Security has quickly become a killer feature for apps, as discussed in our article about [Multipeer Connectivity](https://nshipster.com/multipeer-connectivity/).
Perhaps configuration profiles, with the ability to embed certificates and single sign-on credentials, could add another level of security to communication apps.
diff --git a/2014-04-14-xcode-plugins.md b/2014-04-14-xcode-plugins.md
index 759ed1f9..e7c4b1ce 100644
--- a/2014-04-14-xcode-plugins.md
+++ b/2014-04-14-xcode-plugins.md
@@ -1,18 +1,29 @@
---
title: Xcode Plugins
-author: Mattt Thompson
+author: Mattt
category: Xcode
-tags: popular
excerpt: "This week on NSHipster: a roundup of some of the most useful and exciting plugins for Xcode—ready for you to try out yourself today!"
+revisions:
+ "2014-04-14": Original publication
+ "2019-03-25": Added deprecation notice
status:
- swift: n/a
+ swift: n/a
---
-Apple is nothing if not consistent. From [Pentalobular screws](http://en.wikipedia.org/wiki/Pentalobe_screw) to [Sandboxing](https://developer.apple.com/app-sandboxing/), customers are simply expected to relinquish a fair amount of control when they choose to buy a Mac or iPhone. Whether these design decisions are made to ensure a good user experience, or this control is exercised as an end in itself is debatable, but the reality is that in both hardware and software, Apple prefers an ivory tower to a bazaar.
+{% error do %}
+As of 2016,
+Xcode Plugins are no longer supported.
+Please see our
+[follow-up article](https://nshipster.com/xcode-source-extensions/)
+for more information about their successors:
+Xcode Source Extensions.
+{% enderror %}
+
+Apple is nothing if not consistent. From [Pentalobular screws](https://en.wikipedia.org/wiki/Pentalobe_screw) to [Sandboxing](https://developer.apple.com/app-sandboxing/), customers are simply expected to relinquish a fair amount of control when they choose to buy a Mac or iPhone. Whether these design decisions are made to ensure a good user experience, or this control is exercised as an end in itself is debatable, but the reality is that in both hardware and software, Apple prefers an ivory tower to a bazaar.
No better example of this can be found with Xcode: the very software that software developers use to build software for the walled ecosystems of iOS & OS X software, _is itself a closed ecosystem_.
-Indeed, significant progress has been made in recent years to break open the developer workflow, from alternative IDEs like [AppCode](http://www.jetbrains.com/objc/?utm_source=nshipster) to build tools like [CocoaPods](http://cocoapods.org), [xctool](http://nshipster.com/xctool/) and [nomad](http://nomad-cli.com). However, the notion that Xcode itself could be customized and extended by mere mortals is extremely recent, and just now starting to pick up steam.
+Indeed, significant progress has been made in recent years to break open the developer workflow, from alternative IDEs like [AppCode](http://www.jetbrains.com/objc/?utm_source=nshipster) to build tools like [CocoaPods](http://cocoapods.org), [xctool](https://nshipster.com/xctool/) and [nomad](http://nomad-cli.com). However, the notion that Xcode itself could be customized and extended by mere mortals is extremely recent, and just now starting to pick up steam.
Xcode has had a plugin architecture going back to when Interface Builder was its own separate app. However, this system was relatively obscure, undocumented, and not widely used by third parties. Despite this, developers like [Delisa Mason](https://twitter.com/kattrali) and [Marin Usalj](https://twitter.com/_supermarin) have done incredible work creating a stable and vibrant ecosystem of third-party Xcode extensions.
@@ -21,15 +32,16 @@ Xcode has had a plugin architecture going back to when Interface Builder was its
This week on NSHipster: a roundup of some of the most useful and exciting plugins for Xcode—ready for you to try out yourself today!
> And since these question come up every time there's an article with pictures:
+>
> 1. The color scheme is [Tomorrow Night](https://github.com/ChrisKempson/Tomorrow-Theme)
> 2. The app used to make animated GIFs is [LICEcap](http://www.cockos.com/licecap/)
-* * *
+---
## Making Xcode More Like `X`
Just as New York became a melting pot of cultures from immigrants arriving at
-[Ellis Island](http://en.wikipedia.org/wiki/Ellis_Island), Xcode has welcomed the tired, poor, huddled masses of developers from every platform and language imaginable. Like those first wave Americans, who settled into their respective ethnic neighborhoods to re-establish their traditions in a new land, so too have new iOS developers brought over their preferred workflows and keybindings.
+[Ellis Island](https://en.wikipedia.org/wiki/Ellis_Island), Xcode has welcomed the tired, poor, huddled masses of developers from every platform and language imaginable. Like those first wave Americans, who settled into their respective ethnic neighborhoods to re-establish their traditions in a new land, so too have new iOS developers brought over their preferred workflows and keybindings.
Perhaps you would appreciate a taste of home in the land of Cupertino.
@@ -39,13 +51,13 @@ Finding it _too easy_ to quit Xcode? Try [XVim](https://github.com/JugglerShu/XV
### SublimeText
-
+
-Do you miss having a code minimap along the right gutter of your editor to put things into perspective? Install [SCXcodeMiniMap](https://github.com/stefanceriu/SCXcodeMiniMap) and never again miss the tree nodes for the forest.
+Do you miss having a code minimap along the right gutter of your editor to put things into perspective? Install [SCXcodeMiniMap](https://github.com/stefanceriu/SCXcodeMiniMap) and never again miss the tree nodes for the forest.
### Atom
-
+
Looking to be more in tune with GitHub? Add the [Show in GitHub / BitBucket](https://github.com/larsxschneider/ShowInGitHub) plugin to open to the selected lines of a file online.
@@ -55,19 +67,19 @@ Rather than waiting with crossed fingers and clenched teeth each June, as Apple
### Add Line Breaks to Issue Navigator
-
+
-An annoyance going back to Xcode 4 has been the truncation of items in the Issues Navigator. Never again be frustrated by surprise ellipses when compiler warnings were just starting to get interesting, with [BBUFullIssueNavigator](https://github.com/neonichu/BBUFullIssueNavigator).
+An annoyance going back to Xcode 4 has been the truncation of items in the Issues Navigator. Never again be frustrated by surprise ellipses when compiler warnings were just starting to get interesting, with [BBUFullIssueNavigator](https://github.com/neonichu/BBUFullIssueNavigator).
### Dismiss Debugging Console When Typing
-
+
Another annoyance going back to Xcode 4 is how the debugging console seems to always get in the way. No more, with [BBUDebuggerTuckAway](https://github.com/neonichu/BBUDebuggerTuckAway). As soon as you start typing in the editor, the debugging window will get out of your way.
### Add ANSI Color Support to Debugging Console
-
+
`ncurses` enthusiasts will no doubt be excited by the [XcodeColors](https://github.com/robbiehanson/XcodeColors) plugin, which adds support for ANSI colors to appear in the debugging console.
@@ -85,19 +97,19 @@ Not being the most verbose language in existence, Objective-C can use all the he
### Autocomplete `switch` Statements
-
+
-Fact: `switch` statements and [`NS_ENUM`](http://nshipster.com/ns_enum-ns_options/) go together like mango and sweet sticky rice. The only way it could be improved would be with [SCXcodeSwitchExpander](https://github.com/stefanceriu/SCXcodeSwitchExpander) with automagically fills out a `case` statement for each value in the enumeration.
+Fact: `switch` statements and [`NS_ENUM`](https://nshipster.com/ns_enum-ns_options/) go together like mango and sweet sticky rice. The only way it could be improved would be with [SCXcodeSwitchExpander](https://github.com/stefanceriu/SCXcodeSwitchExpander) with automagically fills out a `case` statement for each value in the enumeration.
### Autocomplete Documentation
-
+
-[Documentation](http://nshipster.com/documentation/) adds a great deal of value to a code base, but it's a tough habit to cultivate. The [VVDocumenter-Xcode](https://github.com/onevcat/VVDocumenter-Xcode) plugin does a great deal to reduce the amount of work necessary to add [appledoc](http://gentlebytes.com/appledoc/)-compatible header documentation. Install it and wrap your code in a loving lexical embrace.
+[Documentation](https://nshipster.com/documentation/) adds a great deal of value to a code base, but it's a tough habit to cultivate. The [VVDocumenter-Xcode](https://github.com/onevcat/VVDocumenter-Xcode) plugin does a great deal to reduce the amount of work necessary to add [appledoc](http://gentlebytes.com/appledoc/)-compatible header documentation. Install it and wrap your code in a loving lexical embrace.
## Formatting Xcode
-["Code organization is a matter of hygiene"](http://nshipster.com/pragma/), so you owe it to yourself and your team to keep whitespace consistent in your code base. Make it easier on yourself by automating the process with these plugins.
+["Code organization is a matter of hygiene"](https://nshipster.com/pragma/), so you owe it to yourself and your team to keep whitespace consistent in your code base. Make it easier on yourself by automating the process with these plugins.
### Code Formatting with ClangFormat
@@ -105,7 +117,7 @@ Fact: `switch` statements and [`NS_ENUM`](http://nshipster.com/ns_enum-ns_option
### Statement Alignment
-
+
Fancy yourself a code designer, automated formatters be damned? [XAlign](https://github.com/qfish/XAlign) automatically aligns assignments _just so_, to appease your most egregious OCD tendencies.
@@ -115,7 +127,7 @@ In a similar vein to what [Bret Victor writes about Learnable Programming](http:
### Inspect `NSColor` / `UIColor` Instances
-
+
Telling what a color is from its RGB values alone is a hard-won skill, so faced with an `NSColor` or `UIColor` value, we have little recourse to know what it'll look like until the code is built and run. Enter [ColorSense for Xcode](https://github.com/omz/ColorSense-for-Xcode)
@@ -125,13 +137,13 @@ Quoth the README:
### Autocomplete Images from Project Bundle
-
+
Similar to the ColorSense plugin, [KSImageNamed](https://github.com/ksuther/KSImageNamed-Xcode) will preview and autocomplete images in `[UIImage imageNamed:]` declarations.
### Semantics Highlighting
-
+
Any editor worth its salt is expected to have some form of syntax highlighting. But [this recent post by Evan Brooks](https://medium.com/p/3a6db2743a1e) presents the idea of _semantic_ highlighting in editors. The idea is that each variable within a scope would be assigned a particular color, which would be consistent across references. This way, one could easily tell the difference between two instance variables in the same method.
@@ -139,14 +151,13 @@ Any editor worth its salt is expected to have some form of syntax highlighting.
### Localization
-
+
-
+
-It's no secret that NSHipster has [a soft spot for localization](http://nshipster.com/nslocalizedstring/). For this reason, this publication is emphatic in its recommendation of [Lin](https://github.com/questbeat/Lin-Xcode5), a clever Xcode plugin that brings the localization editor to your code.
+It's no secret that NSHipster has [a soft spot for localization](https://nshipster.com/nslocalizedstring/). For this reason, this publication is emphatic in its recommendation of [Lin](https://github.com/questbeat/Lin-Xcode5), a clever Xcode plugin that brings the localization editor to your code.
-
-* * *
+---
Xcode's plugin architecture is based on a number of private frameworks specific to Xcode, including DVTKit & IDEKit. A [complete list](https://github.com/luisobo/Xcode5-RuntimeHeaders) can be derived by running [`class-dump`](http://stevenygard.com/projects/class-dump/) on the Xcode app bundle.
diff --git a/2014-04-21-uiactivityviewcontroller.md b/2014-04-21-uiactivityviewcontroller.md
index bf236034..93c84ff8 100644
--- a/2014-04-21-uiactivityviewcontroller.md
+++ b/2014-04-21-uiactivityviewcontroller.md
@@ -1,309 +1,268 @@
---
title: UIActivityViewController
-author: Mattt Thompson
+author: Mattt
category: Cocoa
-excerpt: "The relationship between code and data has long been a curious one."
+excerpt: >-
+ iOS provides a unified interface for users to
+ share and perform actions on strings, images, URLs,
+ and other items within an app.
+revisions:
+ "2014-03-31": Original publication
+ "2018-12-05": Updated for iOS 12 and Swift 4.2
status:
- swift: 2.0
- reviewed: September 7, 2015
+ swift: 4.2
+ reviewed: December 5, 2018
---
-The relationship between code and data has long been a curious one.
-
-Certain programming languages, such as [Lisp](http://en.wikipedia.org/wiki/Lisp_programming_language), [Io](http://en.wikipedia.org/wiki/Io_%28programming_language%29), and [Mathematica](http://en.wikipedia.org/wiki/Mathematica) are [homoiconic](http://en.wikipedia.org/wiki/Homoiconicity), meaning that their code is represented as a data primitive, which itself can be manipulated in code. Most other languages, including Objective-C, however, create a strict boundary between the two, shying away from `eval()` and other potentially dangerous methods of dynamic instructing loading.
-
-This tension between code and data is brought to a whole new level when the data in question is too large or unwieldy to represent as anything but a byte stream. The question of how to encode, decode, and interpret the binary representation of images, documents, and media has been ongoing since the very first operating systems.
-
-The Core Services framework on OS X and Mobile Core Services framework on iOS provide functions that identify and categorize data types by file extension and [MIME type](http://en.wikipedia.org/wiki/Internet_media_type), according to [Universal Type Identifiers](http://en.wikipedia.org/wiki/Uniform_Type_Identifier). UTIs provide an extensible, hierarchical categorization system, which affords the developer great flexibility in handling even the most exotic file types. For example, a Ruby source file (`.rb`) is categorized as Ruby Source Code > Source Code > Text > Content > Data; a QuickTime Movie file (`.mov`) is categorized as Video > Movie > Audiovisual Content > Content > Data.
-
-UTIs have worked reasonably well within the filesystem abstraction of the desktop. However, in a mobile paradigm, where files and directories are hidden from the user, this breaks down quickly. And, what's more, the rise of cloud services and social media has placed greater importance on remote entities over local files. Thus, a tension between UTIs and URLs.
-
-It's clear that we need something else. Could `UIActivityViewController` be the solution we so desperately seek?
-
-* * *
-
-`UIActivityViewController`, introduced in iOS 6, provides a unified services interface for sharing and performing actions on data within an application.
-
-Given a collection of actionable data, a `UIActivityViewController` instance is created as follows:
-
-~~~{swift}
-let string: String = ...
-let URL: NSURL = ...
-
-let activityViewController = UIActivityViewController(activityItems: [string, URL], applicationActivities: nil)
-navigationController?.presentViewController(activityViewController, animated: true) {
- // ...
+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.
+
+```swift
+let string = "Hello, world!"
+let url = URL(string: "https://nshipster.com")!
+let image = UIImage(named: "mustache.jpg")
+let pdf = Bundle.main.url(forResource: "Q4 Projections",
+ withExtension: "pdf")
+
+let activityViewController =
+ UIActivityViewController(activityItems: [string, url, image, pdf],
+ applicationActivities: nil)
+
+present(activityViewController, animated: true) {
+ <#...#>
}
-~~~
+```
-~~~{objective-c}
-NSString *string = ...;
-NSURL *URL = ...;
+When you run this code
+the following is presented on the screen:
-UIActivityViewController *activityViewController =
- [[UIActivityViewController alloc] initWithActivityItems:@[string, URL]
- applicationActivities:nil];
-[navigationController presentViewController:activityViewController
- animated:YES
- completion:^{
- // ...
-}];
-~~~
+{% asset uiactivityviewcontroller.png alt="UIActivityViewController" %}
-This would present the following at the bottom of the screen:
+By default,
+`UIActivityViewController` shows all the activities available
+for the items provided,
+but you can exclude certain activity types
+via the `excludedActivityTypes` property.
-
-
-By default, `UIActivityViewController` will show all available services supporting the provided items, but certain activity types can be excluded:
-
-~~~{swift}
-activityViewController.excludedActivityTypes = [UIActivityTypePostToFacebook]
-~~~
-
-~~~{objective-c}
-activityViewController.excludedActivityTypes = @[UIActivityTypePostToFacebook];
-~~~
+```swift
+activityViewController.excludedActivityTypes = [.postToFacebook]
+```
Activity types are divided up into "action" and "share" types:
-### UIActivityCategoryAction
+- **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.
-- `UIActivityTypePrint`
-- `UIActivityTypeCopyToPasteboard`
-- `UIActivityTypeAssignToContact`
-- `UIActivityTypeSaveToCameraRoll`
-- `UIActivityTypeAddToReadingList`
-- `UIActivityTypeAirDrop`
+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.
-### UIActivityCategoryShare
+The following tables show the available activity types for each category
+and their supported items:
-- `UIActivityTypeMessage`
-- `UIActivityTypeMail`
-- `UIActivityTypePostToFacebook`
-- `UIActivityTypePostToTwitter`
-- `UIActivityTypePostToFlickr`
-- `UIActivityTypePostToVimeo`
-- `UIActivityTypePostToTencentWeibo`
-- `UIActivityTypePostToWeibo`
-
-Each activity type supports a number of different data types. For example, a Tweet might be composed of an `NSString`, along with an attached image and/or URL.
-
-### Supported Data Types by Activity Type
+### UIActivityCategoryAction
-
-## `` & `UIActivityItemProvider`
-
-Similar to how a [pasteboard item](https://developer.apple.com/library/mac/documentation/cocoa/reference/NSPasteboardItem_Class/Reference/Reference.html) can be used to provide data only when necessary, in order to avoid excessive memory allocation or processing time, activity items can be of a custom type.
-
-Any object conforming to ``, including the built-in `UIActivityItemProvider` class, can be used to dynamically provide different kinds of data depending on the activity type.
-
-### ``
-
-#### Getting the Data Items
-
-- `activityViewControllerPlaceholderItem:`
-- `activityViewController:itemForActivityType:`
-
-#### Providing Information About the Data Items
-
-- `activityViewController:subjectForActivityType:`
-- `activityViewController:dataTypeIdentifierForActivityType:`
-- `activityViewController:thumbnailImageForActivityType:suggestedSize:`
-
-One example of how this could be used is to customize a message, depending on whether it's to be shared on Facebook or Twitter.
-
-~~~{swift}
-func activityViewController(activityViewController: UIActivityViewController, itemForActivityType activityType: String) -> AnyObject? {
- if activityType == UIActivityTypePostToFacebook {
- return NSLocalizedString("Like this!", comment: "comment")
- } else if activityType == UIActivityTypePostToTwitter {
- return NSLocalizedString("Retweet this!", comment: "comment")
- } else {
- return nil
- }
-}
-~~~
-
-~~~{objective-c}
-- (id)activityViewController:(UIActivityViewController *)activityViewController
- itemForActivityType:(NSString *)activityType
-{
- if ([activityType isEqualToString:UIActivityTypePostToFacebook]) {
- return NSLocalizedString(@"Like this!");
- } else if ([activityType isEqualToString:UIActivityTypePostToTwitter]) {
- return NSLocalizedString(@"Retweet this!");
- } else {
- return nil;
- }
-}
-~~~
+{% info %}
+`UIActivityViewController` allows users to choose how they share content.
+However, as a developer,
+you can access this functionality directly.
+Here are the corresponding APIs for each of the system-provided activity types:
+
+| Activity Type | Corresponding API |
+| ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
+| `addToReadingList` | [`SSReadingList`](https://developer.apple.com/documentation/safariservices/ssreadinglist/1621226-additem) |
+| `assignToContact` | [`CNContact`](https://developer.apple.com/documentation/contacts/cncontact) |
+| `copyToPasteboard` | [`UIPasteboard`](https://developer.apple.com/documentation/uikit/uipasteboard/1829417-setitems) |
+| `print` | [`UIPrintInteractionController`](https://developer.apple.com/documentation/uikit/uiprintinteractioncontroller/1618174-printtoprinter) |
+| `saveToCameraRoll` | [`UIImageWriteToSavedPhotosAlbum`](https://developer.apple.com/documentation/uikit/1619125-uiimagewritetosavedphotosalbum) |
+| `mail` | [`MFMailComposeViewController`](https://developer.apple.com/documentation/messageui/mfmailcomposeviewcontroller) |
+| `message` | [`MFMessageComposeViewController`](https://developer.apple.com/documentation/messageui/mfmessagecomposeviewcontroller) |
+| `postToFacebook` `postToFlickr` `postToTencentWeibo` `postToTwitter` `postToVimeo` `postToWeibo` | [`SLComposeViewController`](https://developer.apple.com/documentation/social/slcomposeviewcontroller) |
+
+{% endinfo %}
## Creating a Custom UIActivity
-In addition to the aforementioned system-provided activities, its possible to create your own activity.
+In addition to the system-provided activities,
+you can create your own activities.
-As an example, let's create a custom activity type that takes an image URL and applies a mustache to it using [mustache.me](http://mustache.me).
+As an example,
+let's create a custom activity
+that takes an image and applies a mustache to it via a web application.
-
+
-
-
+
+
Before
@@ -311,355 +270,195 @@ As an example, let's create a custom activity type that takes an image URL and a
-First, we define a [reverse-DNS identifier](http://en.wikipedia.org/wiki/Reverse_domain_name_notation) for the activity type:
-
-~~~{swift}
-let HIPMustachifyActivityType = "com.nshipster.activity.Mustachify"
-~~~
-
-~~~{objective-c}
-static NSString * const HIPMustachifyActivityType = @"com.nshipster.activity.Mustachify";
-~~~
+### Defining a Custom Activity Type
-Then specify the category as `UIActivityCategoryAction` and provide a localized title & iOS version appropriate image:
+First,
+define a new activity type constant
+in an extension to `UIActivity.ActivityType`,
+initialized with a
+[reverse-DNS identifier](https://en.wikipedia.org/wiki/Reverse_domain_name_notation).
-~~~{swift}
-// MARK: - UIActivity
-
-override class func activityCategory() -> UIActivityCategory {
- return .Action
+```swift
+extension UIActivity.ActivityType {
+ static let mustachify =
+ UIActivity.ActivityType("com.nshipster.mustachify")
}
+```
-override func activityType() -> String? {
- return HIPMustachifyActivityType
-}
+### Creating a UIActivity Subclass
-override func activityTitle() -> String? {
- return NSLocalizedString("Mustachify", comment: "comment")
-}
+Next,
+create a subclass of `UIActivity`
+and override the default implementations of the
+`activityCategory` type property
+and `activityType`, `activityTitle`, and `activityImage` instance properties.
-override func activityImage() -> UIImage? {
- if #available(iOS 7.0, *) {
- return UIImage(named: "MustachifyUIActivity7")
- } else {
- return UIImage(named: "MustachifyUIActivity")
+```swift
+class MustachifyActivity: UIActivity {
+ override class var activityCategory: UIActivity.Category {
+ return .action
}
-}
-~~~
-
-~~~{objective-c}
-#pragma mark - UIActivity
-
-+ (UIActivityCategory)activityCategory {
- return UIActivityCategoryAction;
-}
-
-- (NSString *)activityType {
- return HIPMustachifyActivityType;
-}
-
-- (NSString *)activityTitle {
- return NSLocalizedString(@"Mustachify", nil);
-}
-- (UIImage *)activityImage {
- if (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_6_1) {
- return [UIImage imageNamed:@"MustachifyUIActivity7"];
- } else {
- return [UIImage imageNamed:@"MustachifyUIActivity"];
+ override var activityType: UIActivity.ActivityType? {
+ return .mustachify
}
-}
-~~~
-
-Next, we create a helper function, `HIPMatchingURLsInActivityItems`, which returns an array of any image URLs of the supported type.
-~~~{swift}
-func HIPMatchingURLsInActivityItems(activityItems: [AnyObject]) -> [AnyObject] {
- return activityItems.filter {
- if let url = $0 as? NSURL where !url.fileURL {
- return url.pathExtension?.caseInsensitiveCompare("jpg") == .OrderedSame
- || url.pathExtension?.caseInsensitiveCompare("png") == .OrderedSame
- }
+ override var activityTitle: String? {
+ return NSLocalizedString("Mustachify", comment: "activity title")
+ }
- return false
+ override var activityImage: UIImage? {
+ return UIImage(named: "mustachify-icon")
}
-}
-~~~
-
-~~~{objective-c}
-static NSArray * HIPMatchingURLsInActivityItems(NSArray *activityItems) {
- return [activityItems filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:
- ^BOOL(id item, __unused NSDictionary *bindings) {
- if ([item isKindOfClass:[NSURL class]] &&
- ![(NSURL *)item isFileURL]) {
- return [[(NSURL *)item pathExtension] caseInsensitiveCompare:@"jpg"] == NSOrderedSame ||
- [[(NSURL *)item pathExtension] caseInsensitiveCompare:@"png"] == NSOrderedSame;
- }
- return NO;
- }]];
+ <#...#>
}
-~~~
+```
-This function is then used in `-canPerformWithActivityItems:` and `prepareWithActivityItems:` to get the mustachio'd image URL of the first PNG or JPEG, if any.
+### Determining Which Items are Actionable
-~~~{swift}
-override func canPerformWithActivityItems(activityItems: [AnyObject]) -> Bool {
- return HIPMatchingURLsInActivityItems(activityItems).count > 0
-}
+Activities are responsible for determining
+whether they can act on a given array of items
+by overriding the `canPerform(withActivityItems:)` method.
-override func prepareWithActivityItems(activityItems: [AnyObject]) {
- let HIPMustachifyMeURLFormatString = "http://mustachify.me/%d?src=%@"
+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:
- if let firstMatch = HIPMatchingURLsInActivityItems(activityItems).first, mustacheType = self.mustacheType {
- imageURL = NSURL(string: String(format: HIPMustachifyMeURLFormatString, [mustacheType, firstMatch]))
+```swift
+override func canPerform(withActivityItems activityItems: [Any]) -> Bool {
+ for case is UIImage in activityItems {
+ return true
}
- // ...
+ return false
}
-~~~
+```
-~~~{objective-c}
-- (BOOL)canPerformWithActivityItems:(NSArray *)activityItems {
- return [HIPMatchingURLsInActivityItems(activityItems) count] > 0;
-}
+### Preparing for Action
-- (void)prepareWithActivityItems:(NSArray *)activityItems {
- static NSString * const HIPMustachifyMeURLFormatString = @"http://mustachify.me/%d?src=%@";
+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.
- self.imageURL = [NSURL URLWithString:[NSString stringWithFormat:HIPMustachifyMeURLFormatString, self.mustacheType, [HIPMatchingURLsInActivityItems(activityItems) firstObject]]];
-}
-~~~
+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:
-Our webservice provides a variety of mustache options, which are defined in an enumeration:
+```swift
+var sourceImageData: Data?
-~~~{swift}
-enum HIPMustacheType: Int {
- case English, Horseshoe, Imperial, Chevron, Natural, Handlebar
-}
-~~~
-
-~~~{objective-c}
-typedef NS_ENUM(NSInteger, HIPMustacheType) {
- HIPMustacheTypeEnglish,
- HIPMustacheTypeHorseshoe,
- HIPMustacheTypeImperial,
- HIPMustacheTypeChevron,
- HIPMustacheTypeNatural,
- HIPMustacheTypeHandlebar,
-};
-~~~
-
-Finally, we provide a `UIViewController` to display the image. For this example, a simple `UIWebView` controller suffices.
-
-~~~{swift}
-class HIPMustachifyWebViewController: UIViewController, UIWebViewDelegate {
- var webView: UIWebView { get }
+override func prepare(withActivityItems activityItems: [Any]) {
+ for case let image as UIImage in activityItems {
+ self.sourceImageData = image.pngData()
+ return
+ }
}
-~~~
-
-~~~{objective-c}
-@interface HIPMustachifyWebViewController : UIViewController
-@property (readonly, nonatomic, strong) UIWebView *webView;
-@end
-~~~
-
-~~~{swift}
-func activityViewController() -> UIViewController {
- let webViewController = HIPMustachifyWebViewController()
-
- let request = NSURLRequest(URL: imageURL)
- webViewController.webView.loadRequest(request)
+```
+
+### 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.
+
+```swift
+var mustachioedImage: UIImage?
+
+override func perform() {
+ let url = URL(string: "https://mustachify.app/")!
+ var request = URLRequest(url: url)
+ request.httpMethod = "POST"
+ request.httpBody = self.sourceImageData
+
+ URLSession.shared.dataTask(with: request) { (data, _, error) in
+ guard error == nil else {
+ self.activityDidFinish(false)
+ return
+ }
- return webViewController
+ if let data = data,
+ let image = UIImage(data: data)
+ {
+ self.mustachioedImage = image
+ self.activityDidFinish(true)
+ } else {
+ self.activityDidFinish(false)
+ }
+ }
}
-~~~
-
-~~~{objective-c}
-- (UIViewController *)activityViewController {
- HIPMustachifyWebViewController *webViewController = [[HIPMustachifyWebViewController alloc] init];
-
- NSURLRequest *request = [NSURLRequest requestWithURL:self.imageURL];
- [webViewController.webView loadRequest:request];
+```
- return webViewController;
-}
-~~~
+### Showing the Results
-To use our brand new mustache activity, we simply pass it in the `UIActivityViewController initializer`:
+The final step is to provide a view controller
+to be presented with the result of our activity.
-~~~{swift}
-let mustacheActivity = HIPMustachifyActivity()
-let activityViewController = UIActivityViewController(activityItems: [imageURL], applicationActivities: [mustacheActivity])
-~~~
+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 the`activityViewController` method.
-~~~{objective-c}
-HIPMustachifyActivity *mustacheActivity = [[HIPMustachifyActivity alloc] init];
-UIActivityViewController *activityViewController =
- [[UIActivityViewController alloc] initWithActivityItems:@[imageURL]
- applicationActivities:@[mustacheActivity]];
-~~~
+```swift
+import QuickLook
-## Invoking Actions Manually
+extension MustachifyActivity: QLPreviewControllerDataSource {
+ override var activityViewController: UIViewController? {
+ guard let image = self.mustachioedImage else {
+ return nil
+ }
-Now is a good time to be reminded that while `UIActivityViewController` allows users to perform actions of their choosing, sharing can still be invoked manually, when the occasion arises.
+ let viewController = QLPreviewController()
+ viewController.dataSource = self
+ return viewController
+ }
-So for completeness, here's how one might go about performing some of these actions manually:
+ // MARK: QLPreviewControllerDataSource
-### Open URL
+ func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
+ return self.mustachioedImage != nil ? 1 : 0
+ }
-~~~{swift}
-if let URL = NSURL(string: "http://nshipster.com") {
- UIApplication.sharedApplication().openURL(URL)
+ func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
+ return self.mustachioedImage!
+ }
}
-~~~
+```
-~~~{objective-c}
-NSURL *URL = [NSURL URLWithString:@"http://nshipster.com"];
-[[UIApplication sharedApplication] openURL:URL];
-~~~
+### Providing a Custom Activity to Users
-System-supported URL schemes include: `mailto:`, `tel:`, `sms:`, and `maps:`.
+We can use our brand new mustache activity
+by passing it to the `applicationActivities` parameter
+in the `UIActivityViewController initializer`:
-### Add to Safari Reading List
+```swift
+let activityViewController =
+ UIActivityViewController(activityItems: [image],
+ applicationActivities: [Mustachify()])
-~~~{swift}
-import SafariServices
-
-if let URL = NSURL(string: "http://nshipster.com/uiactivityviewcontroller") {
- let _ = try? SSReadingList.defaultReadingList()?.addReadingListItemWithURL(URL,
- title: "NSHipster",
- previewText: "..."
- )
-}
-~~~
-
-~~~{objective-c}
-@import SafariServices;
-
-NSURL *URL = [NSURL URLWithString:@"http://nshipster.com/uiactivityviewcontroller"];
-[[SSReadingList defaultReadingList] addReadingListItemWithURL:URL
- title:@"NSHipster"
- previewText:@"..."
- error:nil];
-~~~
-
-### Add to Saved Photos
-
-~~~{swift}
-let image: UIImage = ...
-let completionTarget: AnyObject = self
-let completionSelector: Selector = "didWriteToSavedPhotosAlbum"
-let contextInfo: UnsafeMutablePointer = nil
-
-UIImageWriteToSavedPhotosAlbum(image, completionTarget, completionSelector, contextInfo)
-~~~
-
-~~~{objective-c}
-UIImage *image = ...;
-id completionTarget = self;
-SEL completionSelector = @selector(didWriteToSavedPhotosAlbum);
-void *contextInfo = NULL;
-UIImageWriteToSavedPhotosAlbum(image, completionTarget, completionSelector, contextInfo);
-~~~
-
-### Send SMS
-
-~~~{swift}
-import MessageUI
-
-let messageComposeViewController = MFMessageComposeViewController()
-messageComposeViewController.messageComposeDelegate = self
-messageComposeViewController.recipients = ["mattt@nshipster•com"]
-messageComposeViewController.body = "Lorem ipsum dolor sit amet"
-navigationController?.presentViewController(messageComposeViewController, animated: true) {
- // ...
-}
-~~~
-
-~~~{objective-c}
-@import MessageUI;
-
-MFMessageComposeViewController *messageComposeViewController = [[MFMessageComposeViewController alloc] init];
-messageComposeViewController.messageComposeDelegate = self;
-messageComposeViewController.recipients = @[@"mattt@nshipster•com"];
-messageComposeViewController.body = @"Lorem ipsum dolor sit amet";
-[navigationController presentViewController:messageComposeViewController animated:YES completion:^{
- // ...
-}];
-~~~
-
-### Send Email
-
-~~~{swift}
-import MessageUI
-
-let mailComposeViewController = MFMailComposeViewController()
-mailComposeViewController.mailComposeDelegate = self
-mailComposeViewController.setToRecipients(["mattt@nshipster•com"])
-mailComposeViewController.setSubject("Hello")
-mailComposeViewController.setMessageBody("Lorem ipsum dolor sit amet", isHTML: false)
-navigationController?.presentViewController(mailComposeViewController, animated: true) {
- // ...
+present(activityViewController, animated: true) {
+ <#...#>
}
-~~~
-
-~~~{objective-c}
-@import MessageUI;
-
-MFMailComposeViewController *mailComposeViewController = [[MFMailComposeViewController alloc] init];
-mailComposeViewController.mailComposeDelegate = self;
-[mailComposeViewController setToRecipients:@[@"mattt@nshipster•com"]];
-[mailComposeViewController setSubject:@"Hello"];
-[mailComposeViewController setMessageBody:@"Lorem ipsum dolor sit amet"
- isHTML:NO];
-[navigationController presentViewController:mailComposeViewController animated:YES completion:^{
- // ...
-}];
-~~~
-
-### Post Tweet
-
-~~~{swift}
-import Social
-
-let tweetComposeViewController = SLComposeViewController(forServiceType: SLServiceTypeTwitter)
-tweetComposeViewController.setInitialText("Lorem ipsum dolor sit amet.")
-navigationController?.presentViewController(tweetComposeViewController, animated: true) {
- // ...
-}
-~~~
-
-~~~{objective-c}
-@import Social;
+```
-SLComposeViewController *tweetComposeViewController = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeTwitter];
-[tweetComposeViewController setInitialText:@"Lorem ipsum dolor sit amet."];
-[self.navigationController presentViewController:tweetComposeViewController
- animated:YES
- completion:^{
- // ...
- }];
-~~~
+{% asset uiactivityviewcontroller-custom-action.png %}
-## IntentKit
-
-While all of this is impressive and useful, there is a particular lacking in the activities paradigm in iOS, when compared to the rich [Intents Model](http://developer.android.com/guide/components/intents-filters.html) found on Android.
-
-On Android, apps can register for different intents, to indicate that they can be used for Maps, or as a Browser, and be selected as the default app for related activities, like getting directions, or bookmarking a URL.
-
-While iOS lacks the extensible infrastructure to support this, a 3rd-party library called [IntentKit](https://github.com/intentkit/IntentKit), by [@lazerwalker](https://github.com/lazerwalker) (of [f*ingblocksyntax.com](http://goshdarnblocksyntax.com) fame), is an interesting example of how we might narrow the gap ourselves.
-
-
-
-Normally, a developer would have to do a lot of work to first, determine whether a particular app is installed, and how to construct a URL to support a particular activity.
-
-IntentKit consolidates the logic of connecting to the most popular Web, Maps, Mail, Twitter, Facebook, and Google+ clients, in a UI similar to `UIActivityViewController`.
-
-Anyone looking to take their sharing experience to the next level should definitely give this a look.
-
-* * *
+---
-There is a strong argument to be made that the longterm viability of iOS as a platform depends on sharing mechanisms like `UIActivityViewController`. As the saying goes, "Information wants to be free". And anything that stands in the way of federation will ultimately lose to something that does not.
+There is a strong argument to be made that
+the long-term viability of iOS as a platform
+depends on sharing mechanisms like `UIActivityViewController`.
-The future prospects of public [remote view controller](http://oleb.net/blog/2012/10/remote-view-controllers-in-ios-6/) APIs gives me hope for the future of sharing on iOS. For now, though, we could certainly do much worse than `UIActivityViewController`.
+As the saying goes, _"Information wants to be free."_
+Anything that stands in the way of federation is doomed to fail.
diff --git a/2014-04-28-mkgeodesicpolyline.md b/2014-04-28-mkgeodesicpolyline.md
index ce070f8f..77f3e456 100644
--- a/2014-04-28-mkgeodesicpolyline.md
+++ b/2014-04-28-mkgeodesicpolyline.md
@@ -1,6 +1,6 @@
---
title: MKGeodesicPolyline
-author: Mattt Thompson
+author: Mattt
category: Cocoa
excerpt: "We knew that the Earth was not flat long before 1492. Early navigators observed the way ships would dip out of view over the horizon many centuries before the Age of Discovery. For many iOS developers, though, a flat MKMapView was a necessary conceit until recently."
status:
@@ -16,15 +16,15 @@ What changed? The discovery of `MKGeodesicPolyline`, which is the subject of thi
* * *
-[`MKGeodesicPolyline`](https://developer.apple.com/library/ios/documentation/MapKit/Reference/MKGeodesicPolyline_class/Reference/Reference.html) was introduced to the Map Kit framework in iOS 7. As its name implies, it creates a [geodesic](http://en.wikipedia.org/wiki/Geodesic)—essentially a straight line over a curved surface.
+[`MKGeodesicPolyline`](https://developer.apple.com/library/ios/documentation/MapKit/Reference/MKGeodesicPolyline_class/Reference/Reference.html) was introduced to the Map Kit framework in iOS 7. As its name implies, it creates a [geodesic](https://en.wikipedia.org/wiki/Geodesic)—essentially a straight line over a curved surface.
-On the surface of a sphereoblate spheroidgeoid, the shortest distance between two points appears as an arc on a flat projection. Over large distances, this takes a [pronounced, circular shape](http://en.wikipedia.org/wiki/Great-circle_distance).
+On the surface of a sphereoblate spheroidgeoid, the shortest distance between two points appears as an arc on a flat projection. Over large distances, this takes a [pronounced, circular shape](https://en.wikipedia.org/wiki/Great-circle_distance).
An `MKGeodesicPolyline` is created with an array of 2 `MKMapPoint`s or `CLLocationCoordinate2D`s:
### Creating an `MKGeodesicPolyline`
-~~~{swift}
+```swift
let LAX = CLLocation(latitude: 33.9424955, longitude: -118.4080684)
let JFK = CLLocation(latitude: 40.6397511, longitude: -73.7789256)
@@ -32,8 +32,8 @@ var coordinates = [LAX.coordinate, JFK.coordinate]
let geodesicPolyline = MKGeodesicPolyline(coordinates: &coordinates, count: 2)
mapView.addOverlay(geodesicPolyline)
-~~~
-~~~{objective-c}
+```
+```objc
CLLocation *LAX = [[CLLocation alloc] initWithLatitude:33.9424955
longitude:-118.4080684];
CLLocation *JFK = [[CLLocation alloc] initWithLatitude:40.6397511
@@ -47,22 +47,22 @@ MKGeodesicPolyline *geodesicPolyline =
count:2];
[mapView addOverlay:geodesicPolyline];
-~~~
+```
Although the overlay looks like a smooth curve, it is actually comprised of thousands of tiny line segments (true to its `MKPolyline` lineage):
-~~~{swift}
+```swift
print(geodesicPolyline.pointCount) // 3984
-~~~
-~~~{objective-c}
+```
+```objc
NSLog(@"%d", geodesicPolyline.pointCount) // 3984
-~~~
+```
Like any object conforming to the `MKOverlay` protocol, an `MKGeodesicPolyline` instance is displayed by adding it to an `MKMapView` with `addOverlay()` and implementing `mapView(_:rendererForOverlay:)`:
### Rendering `MKGeodesicPolyline` on an `MKMapView`
-~~~{swift}
+```swift
// MARK: MKMapViewDelegate
func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
@@ -77,8 +77,8 @@ func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOve
return renderer
}
-~~~
-~~~{objective-c}
+```
+```objc
#pragma mark - MKMapViewDelegate
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView
@@ -96,15 +96,15 @@ func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOve
return renderer;
}
-~~~
+```
-
+
-> For comparison, here's the same geodesic overlaid with a route created from [`MKDirections`](http://nshipster.com/mktileoverlay-mkmapsnapshotter-mkdirections/):
+> For comparison, here's the same geodesic overlaid with a route created from [`MKDirections`](https://nshipster.com/mktileoverlay-mkmapsnapshotter-mkdirections/):
-
+
-[As the crow flies](http://en.wikipedia.org/wiki/As_the_crow_flies), it's 3,983 km.
+[As the crow flies](https://en.wikipedia.org/wiki/As_the_crow_flies), it's 3,983 km.
As the wolf runs, it's 4,559 km—nearly 15% longer.
…and that's just distance; taking into account average travel speed, the total time is ~5 hours by air and 40+ hours by land.
@@ -114,43 +114,43 @@ Since geodesics make reasonable approximations for flight paths, a common use ca
To do this, we'll make properties for our map view and geodesic polyline between LAX and JFK, and add new properties for the `planeAnnotation` and `planeAnnotationPosition` (the index of the current map point for the polyline):
-~~~{swift}
+```swift
// MARK: Flight Path Properties
var mapView: MKMapView!
var flightpathPolyline: MKGeodesicPolyline!
var planeAnnotation: MKPointAnnotation!
var planeAnnotationPosition = 0
-~~~
-~~~{objective-c}
+```
+```objc
@interface MapViewController ()
@property MKMapView *mapView;
@property MKGeodesicPolyline *flightpathPolyline;
@property MKPointAnnotation *planeAnnotation;
@property NSUInteger planeAnnotationPosition;
@end
-~~~
+```
Next, right below the initialization of our map view and polyline, we create an `MKPointAnnotation` for our plane:
-~~~{swift}
+```swift
let annotation = MKPointAnnotation()
annotation.title = NSLocalizedString("Plane", comment: "Plane marker")
mapView.addAnnotation(annotation)
self.planeAnnotation = annotation
self.updatePlanePosition()
-~~~
-~~~{objective-c}
+```
+```objc
self.planeAnnotation = [[MKPointAnnotation alloc] init];
self.planeAnnotation.title = NSLocalizedString(@"Plane", nil);
[self.mapView addAnnotation:self.planeAnnotation];
[self updatePlanePosition];
-~~~
+```
That call to `updatePlanePosition` in the last line ticks the animation and updates the position of the plane:
-~~~{swift}
+```swift
func updatePlanePosition() {
let step = 5
guard planeAnnotationPosition + step < flightpathPolyline.pointCount
@@ -164,8 +164,8 @@ func updatePlanePosition() {
performSelector("updatePlanePosition", withObject: nil, afterDelay: 0.03)
}
-~~~
-~~~{objective-c}
+```
+```objc
- (void)updatePlanePosition {
static NSUInteger const step = 5;
@@ -180,13 +180,13 @@ func updatePlanePosition() {
[self performSelector:@selector(updatePlanePosition) withObject:nil afterDelay:0.03];
}
-~~~
+```
We'll perform this method roughly 30 times a second, until the plane has arrived at its final destination.
Finally, we implement `mapView(_:viewForAnnotation:)` to have the annotation render on the map view:
-~~~{swift}
+```swift
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
let planeIdentifier = "Plane"
@@ -197,8 +197,8 @@ func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) ->
return annotationView
}
-~~~
-~~~{objective-c}
+```
+```objc
- (MKAnnotationView *)mapView:(MKMapView *)mapView
viewForAnnotation:(id )annotation
{
@@ -213,9 +213,9 @@ func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) ->
return annotationView;
}
-~~~
+```
-
+
Hmm… close but no [SkyMall Personalized Cigar Case Flask](http://www.skymall.com/personalized-cigar-case-flask/GC900.html).
@@ -225,47 +225,47 @@ Let's update the rotation of the plane as it moves across its flightpath.
To calculate the plane's direction, we'll take the slope from the previous and next points:
-~~~{swift}
+```swift
let previousMapPoint = points[planeAnnotationPosition]
planeAnnotationPosition += step
let nextMapPoint = points[planeAnnotationPosition]
self.planeDirection = directionBetweenPoints(previousMapPoint, nextMapPoint)
self.planeAnnotation.coordinate = MKCoordinateForMapPoint(nextMapPoint)
-~~~
-~~~{objective-c}
+```
+```objc
MKMapPoint previousMapPoint = self.flightpathPolyline.points[self.planeAnnotationPosition];
self.planeAnnotationPosition += step;
MKMapPoint nextMapPoint = self.flightpathPolyline.points[self.planeAnnotationPosition];
self.planeDirection = XXDirectionBetweenPoints(previousMapPoint, nextMapPoint);
self.planeAnnotation.coordinate = MKCoordinateForMapPoint(nextMapPoint);
-~~~
+```
`directionBetweenPoints` is a function that returns a `CLLocationDirection` (0 – 360 degrees, where North = 0) given two `MKMapPoint`s.
> We calculate from `MKMapPoint`s rather than converted coordinates, because we're interested in the slope of the line on the flat projection.
-~~~{swift}
+```swift
private func directionBetweenPoints(sourcePoint: MKMapPoint, _ destinationPoint: MKMapPoint) -> CLLocationDirection {
let x = destinationPoint.x - sourcePoint.x
let y = destinationPoint.y - sourcePoint.y
return radiansToDegrees(atan2(y, x)) % 360 + 90
}
-~~~
-~~~{objective-c}
+```
+```objc
static CLLocationDirection XXDirectionBetweenPoints(MKMapPoint sourcePoint, MKMapPoint destinationPoint) {
double x = destinationPoint.x - sourcePoint.x;
double y = destinationPoint.y - sourcePoint.y;
return fmod(XXRadiansToDegrees(atan2(y, x)), 360.0f) + 90.0f;
}
-~~~
+```
That convenience function `radiansToDegrees` (and its partner, `degreesToRadians`) are simply:
-~~~{swift}
+```swift
private func radiansToDegrees(radians: Double) -> Double {
return radians * 180 / M_PI
}
@@ -273,8 +273,8 @@ private func radiansToDegrees(radians: Double) -> Double {
private func degreesToRadians(degrees: Double) -> Double {
return degrees * M_PI / 180
}
-~~~
-~~~{objective-c}
+```
+```objc
static inline double XXRadiansToDegrees(double radians) {
return radians * 180.0f / M_PI;
}
@@ -282,26 +282,26 @@ static inline double XXRadiansToDegrees(double radians) {
static inline double XXDegreesToRadians(double degrees) {
return degrees * M_PI / 180.0f;
}
-~~~
+```
That direction is stored in a new property, `var planeDirection: CLLocationDirection`, calculated from `self.planeDirection = directionBetweenPoints(currentMapPoint, nextMapPoint)` in `updatePlanePosition` (ideally renamed to `updatePlanePositionAndDirection` with this addition). To make the annotation rotate, we apply a `transform` on `annotationView`:
-~~~{swift}
+```swift
annotationView.transform = CGAffineTransformRotate(mapView.transform,
degreesToRadians(planeDirection))
-~~~
-~~~{objective-c}
+```
+```objc
self.annotationView.transform =
CGAffineTransformRotate(self.mapView.transform,
XXDegreesToRadians(self.planeDirection));
-~~~
+```
-
+
Ah much better! At last, we have mastered the skies with a fancy visualization, worthy of any travel-related app.
* * *
-Perhaps more than any other system framework, MapKit has managed to get incrementally better, little by little with every iOS release [[1]](http://nshipster.com/mktileoverlay-mkmapsnapshotter-mkdirections/) [[2]](http://nshipster.com/mklocalsearch/). For anyone with a touch-and-go relationship to the framework, returning after a few releases is a delightful experience of discovery and rediscovery.
+Perhaps more than any other system framework, MapKit has managed to get incrementally better, little by little with every iOS release [[1]](https://nshipster.com/mktileoverlay-mkmapsnapshotter-mkdirections/) [[2]](https://nshipster.com/mklocalsearch/). For anyone with a touch-and-go relationship to the framework, returning after a few releases is a delightful experience of discovery and rediscovery.
I look forward to seeing what lies on the horizon with iOS 8 and beyond.
diff --git a/2014-05-05-ibaction-iboutlet-iboutletcollection.md b/2014-05-05-ibaction-iboutlet-iboutletcollection.md
index c7b59e14..8801f248 100644
--- a/2014-05-05-ibaction-iboutlet-iboutletcollection.md
+++ b/2014-05-05-ibaction-iboutlet-iboutletcollection.md
@@ -1,13 +1,13 @@
---
title: "IBAction / IBOutlet / IBOutletCollection"
-author: Mattt Thompson
+author: Mattt
category: Cocoa
excerpt: "In programming, what often begins as a necessary instruction eventually becomes a vestigial cue for humans. For developers just starting with Cocoa & Cocoa Touch, the IBAction, IBOutlet, and IBOutletCollection macros are particularly bewildering examples of this phenomenon"
status:
swift: t.b.c.
---
-In programming, what often begins as a necessary instruction eventually becomes a vestigial cue for humans. In the case of Objective-C, [`#pragma` directives](http://nshipster.com/pragma/), [method type encodings](http://nshipster.com/type-encodings/), and all but the most essential [storage classes](http://nshipster.com/c-storage-classes/) have been rendered essentially meaningless, as the compiler becomes increasingly sophisticated. Discarded and disregarded during the compilation phase, they nonetheless remain useful to the development process as a whole, insofar as what they can tell other developers about the code itself.
+In programming, what often begins as a necessary instruction eventually becomes a vestigial cue for humans. In the case of Objective-C, [`#pragma` directives](https://nshipster.com/pragma/), [method type encodings](https://nshipster.com/type-encodings/), and all but the most essential [storage classes](https://nshipster.com/c-storage-classes/) have been rendered essentially meaningless, as the compiler becomes increasingly sophisticated. Discarded and disregarded during the compilation phase, they nonetheless remain useful to the development process as a whole, insofar as what they can tell other developers about the code itself.
For developers just starting with Cocoa & Cocoa Touch, the `IBAction`, `IBOutlet`, and `IBOutletCollection` macros are particularly bewildering examples of this phenomenon. With their raison d'être obscured by decades of change, confusion by anyone without sufficient context is completely understandable.
@@ -15,25 +15,25 @@ As we'll learn in this week's article, though having outgrown their technical ne
* * *
-Unlike other [two-letter prefixes](http://nshipster.com/namespacing/), `IB` does not refer to a system framework, but rather Interface Builder.
+Unlike other [two-letter prefixes](https://nshipster.com/namespacing/), `IB` does not refer to a system framework, but rather Interface Builder.
-[Interface Builder](http://en.wikipedia.org/wiki/Interface_Builder) can trace its roots to the halcyon days of Objective-C, when it and Project Builder comprised the NeXTSTEP developer tools (circa 1988). Before it was subsumed into Xcode 4, Interface Builder remained remarkably unchanged from its 1.0 release. An iOS developer today would feel right at home on a NeXTSTEP workstation, control-dragging views into outlets.
+[Interface Builder](https://en.wikipedia.org/wiki/Interface_Builder) can trace its roots to the halcyon days of Objective-C, when it and Project Builder comprised the NeXTSTEP developer tools (circa 1988). Before it was subsumed into Xcode 4, Interface Builder remained remarkably unchanged from its 1.0 release. An iOS developer today would feel right at home on a NeXTSTEP workstation, control-dragging views into outlets.
-Back when they were separate applications, it was a challenge to keep the object graph represented in a `.nib` document in Interface Builder synchronized with its corresponding `.h` & `.m` files in [Project Builder](http://en.wikipedia.org/wiki/Project_Builder) (what would eventually become Xcode). `IBOutlet` and `IBAction` were used as keywords, to denote what parts of the code should be visible to Interface Builder.
+Back when they were separate applications, it was a challenge to keep the object graph represented in a `.nib` document in Interface Builder synchronized with its corresponding `.h` & `.m` files in [Project Builder](https://en.wikipedia.org/wiki/Project_Builder) (what would eventually become Xcode). `IBOutlet` and `IBAction` were used as keywords, to denote what parts of the code should be visible to Interface Builder.
`IBAction` and `IBOutlet` are, themselves, computationally meaningless, as their macro definitions (in `UINibDeclarations.h`) demonstrate:
-~~~{objective-c}
+```objc
#define IBAction void
#define IBOutlet
-~~~
+```
-> Well actually, there's more than meets the eye. Scrying the [Clang source code](https://llvm.org/svn/llvm-project/cfe/trunk/test/SemaObjC/iboutlet.m), we see that they're actually defined by [__attribute__](http://nshipster.com/__attribute__/)-backed attributes:
+> Well actually, there's more than meets the eye. Scrying the [Clang source code](https://llvm.org/svn/llvm-project/cfe/trunk/test/SemaObjC/iboutlet.m), we see that they're actually defined by [__attribute__](https://nshipster.com/__attribute__/)-backed attributes:
-~~~{objective-c}
+```objc
#define IBOutlet __attribute__((iboutlet))
#define IBAction __attribute__((ibaction))
-~~~
+```
## IBAction
@@ -52,7 +52,7 @@ Thanks to strong, and often compiler-enforced conventions, naming is especially
For example:
-~~~{objective-c}
+```objc
// YES
- (IBAction)refresh:(id)sender;
@@ -63,7 +63,7 @@ For example:
- (IBAction)peformSomeAction;
- (IBAction)didTapButton:(id)sender;
-~~~
+```
## IBOutlet
@@ -75,7 +75,7 @@ An `IBOutlet` connection is usually established between a view or control and it
As with anything in modern Objective-C, **properties are preferred to direct ivar access**. The same is true of `IBOutlet`s:
-~~~{objective-c}
+```objc
// YES
@interface GallantViewController : UIViewController
@property (nonatomic, weak) IBOutlet UISwitch *switch;
@@ -86,7 +86,7 @@ As with anything in modern Objective-C, **properties are preferred to direct iva
IBOutlet UISwitch *_switch
}
@end
-~~~
+```
Since properties are the conventional way to expose and access members of a class, both externally and internally, they are preferred in this case as well, if only for consistency.
@@ -111,36 +111,36 @@ The reason why most `IBOutlet` views can get away with `weak` ownership is that
`IBOutletCollection` is `#define`'d in `UINibDeclarations.h` as:
-~~~{objective-c}
+```objc
#define IBOutletCollection(ClassName)
-~~~
+```
…which is defined in a much more satisfying way, again, [in the Clang source code](http://opensource.apple.com/source/clang/clang-318.0.45/src/tools/clang/test/SemaObjC/iboutletcollection-attr.m):
-~~~{objective-c}
+```objc
#define IBOutletCollection(ClassName) __attribute__((iboutletcollection(ClassName)))
-~~~
+```
-Unlike `IBAction` or `IBOutlet`, `IBOutletCollection` takes a class name as an argument, which is, incidentally, as close to Apple-sanctioned [generics](http://en.wikipedia.org/wiki/Generic_programming) as one gets in Objective-C.
+Unlike `IBAction` or `IBOutlet`, `IBOutletCollection` takes a class name as an argument, which is, incidentally, as close to Apple-sanctioned [generics](https://en.wikipedia.org/wiki/Generic_programming) as one gets in Objective-C.
As a top-level object, an `IBOutletCollection` `@property` should be declared `strong`, with an `NSArray *` type:
-~~~{objective-c}
+```objc
@property (nonatomic, strong) IBOutletCollection(UIButton) NSArray *buttons;
-~~~
+```
There are two rather curious things to note about an `IBOutletCollection` array:
- **Its order is not necessarily guaranteed**. The order of an outlet collection appears to be roughly the order in which their connections are established in Interface Builder. However, there are numerous reports of that order changing across versions of Xcode, or as a natural consequence of version control. Nonetheless, having code rely on a fixed order is strongly discouraged.
- **No matter what type is declared for the property, an `IBOutletCollection` is always an `NSArray`**. In fact, any type can be declared: `NSSet *`, `id `—heck, even `UIColor *` (depending on your error flags)! No matter what you put, an `IBOutletCollection` will always be stored as an `NSArray`, so you might as well have that type match up in your declaration to avoid compiler warnings.
-With the advent of Objective-C [object literals](http://nshipster.com/at-compiler-directives/), `IBOutletCollection` has fallen slightly out of favor—at least for the common use case of convenience accessors, as in:
+With the advent of Objective-C [object literals](https://nshipster.com/at-compiler-directives/), `IBOutletCollection` has fallen slightly out of favor—at least for the common use case of convenience accessors, as in:
-~~~{objective-c}
+```objc
for (UILabel *label in labels) {
label.font = [UIFont systemFontOfSize:14];
}
-~~~
+```
Since declaring a collection of outlets is now as easy as comma-delimiting them within `@[]`, it may make just as much sense to do that as create a distinct collection.
diff --git a/2014-05-12-nshipster-quiz-5.md b/2014-05-12-nshipster-quiz-5.md
index 34e07c37..7f1aa133 100644
--- a/2014-05-12-nshipster-quiz-5.md
+++ b/2014-05-12-nshipster-quiz-5.md
@@ -1,6 +1,6 @@
---
title: "NSHipster Quiz #5"
-author: Mattt Thompson
+author: Mattt
category: Trivia
excerpt: "This fifth incarnation of the NSHipster Quiz took on a distinct North-of-the-Border flavor, as part of the NSNorth conference in Ottawa, Ontario. Think you're up to the challenge, eh?"
status:
@@ -21,104 +21,98 @@ As always, you can play along at home or at work with your colleagues. Here are
- Play with up to 5 friends for maximum enjoyment
- Don't be lame and look things up on the Internet or in Xcode
-* * *
+---
-Round 1: General Knowledge
---------------------------
+## Round 1: General Knowledge
Current events, miscellaneous tidbits, and random trivia. Following a time-honored traditions for NSHipster quizzes, the first round is always a mis-mash of people, places, and pop culture.
-1. In 2011, Apple deprecated OS X's Common Data Security Architecture, leaving them unaffected by what recent vulnerability.
-2. According to rumors, Apple will be partnering with which company to add song recognition functionality to Siri in iOS 8?
-3. The White House expressed disappointment over a "selfie" of Boston Red Sox player David Ortiz and President Obama, due to allegations that it was a promotional stunt for which company?
-4. In Sony's forthcoming Steve Jobs biopic, which actor was recently rumored to being approached by director Danny Boyle to play the lead role? For a bonus point: which actor was previously confirmed for this role, before director David Fincher backed out of the project?
-5. In Apple's Q2 Earnings call, Tim Cook announced the company had acquired 24 companies so far in 2014, including Burstly, which is better known for what service for iOS developers?
-6. After a rumored $3.2 billion acquisition bid by Apple, which American record producer, rapper and entrepreneur has described himself as "the first billionaire in hip hop"? For a bonus point: what is his _legal_ (i.e. non-stage) name?
-7. A widespread (and recently debunked) rumor of Apple announcing Lightning-cable-powered biometric ear buds was originally disclosed on which social network for Silicon Valley insiders?
-8. Oracle won an important victory in the U.S. Court of Appeals against Google in their suit regarding copyright claims of what?
-9. What hot new social networking app allows you to anonymously chat with patrons of its eponymous, popular American chain restaurant?
+1. In 2011, Apple deprecated OS X's Common Data Security Architecture, leaving them unaffected by what recent vulnerability.
+2. According to rumors, Apple will be partnering with which company to add song recognition functionality to Siri in iOS 8?
+3. The White House expressed disappointment over a "selfie" of Boston Red Sox player David Ortiz and President Obama, due to allegations that it was a promotional stunt for which company?
+4. In Sony's forthcoming Steve Jobs biopic, which actor was recently rumored to being approached by director Danny Boyle to play the lead role? For a bonus point: which actor was previously confirmed for this role, before director David Fincher backed out of the project?
+5. In Apple's Q2 Earnings call, Tim Cook announced the company had acquired 24 companies so far in 2014, including Burstly, which is better known for what service for iOS developers?
+6. After a rumored $3.2 billion acquisition bid by Apple, which American record producer, rapper and entrepreneur has described himself as "the first billionaire in hip hop"? For a bonus point: what is his _legal_ (i.e. non-stage) name?
+7. A widespread (and recently debunked) rumor of Apple announcing Lightning-cable-powered biometric ear buds was originally disclosed on which social network for Silicon Valley insiders?
+8. Oracle won an important victory in the U.S. Court of Appeals against Google in their suit regarding copyright claims of what?
+9. What hot new social networking app allows you to anonymously chat with patrons of its eponymous, popular American chain restaurant?
10. If one were to sit down at a NeXTstation and open "/NextLibrary/Frameworks/AppKit.framework/Resources/", they would find the file "NSShowMe.tiff". Who is pictured in this photo?
-
+
-Round 2: Core Potpourri
------------------------
+## Round 2: Core Potpourri
With the fluff out of the way, it's now time to dive into some hardcore Cocoa fundamentals. How well do _you_ know the standard library?
-1. What unit does a Bluetooth peripheral measure RSSI, or received signal strength intensity in?
-2. What is the return value of the following code: `UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, @"image/jpeg", NULL)`?
-3. What function must be called before calling `SecTrustGetCertificateCount` on a `SecTrustRef`?
-4. What UIKit class can be used to show the definition of a word?
-5. An `SCNetworkReachabilityRef` can be created from three different sets of arguments. Fill in the blank `SCNetworkReachabilityCreateWith_______`. (1 pt. each)
-6. `mach_absolute_time()` returns a count of Mach absolute time units. What function can be used to convert this into something more useful, like nanoseconds?
-7. How many arguments does `CGRectDivide` take?
-8. What function would you call to generate a random integer between `1` and `N`
-9. What CoreFoundation function can, among other things, transliterate between different writing systems?
+1. What unit does a Bluetooth peripheral measure RSSI, or received signal strength intensity in?
+2. What is the return value of the following code: `UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, @"image/jpeg", NULL)`?
+3. What function must be called before calling `SecTrustGetCertificateCount` on a `SecTrustRef`?
+4. What UIKit class can be used to show the definition of a word?
+5. An `SCNetworkReachabilityRef` can be created from three different sets of arguments. Fill in the blank `SCNetworkReachabilityCreateWith_______`. (1 pt. each)
+6. `mach_absolute_time()` returns a count of Mach absolute time units. What function can be used to convert this into something more useful, like nanoseconds?
+7. How many arguments does `CGRectDivide` take?
+8. What function would you call to generate a random integer between `1` and `N`
+9. What CoreFoundation function can, among other things, transliterate between different writing systems?
10. What is LLVM's logo? And, for a bonus point: What is GCC's logo?
-Activity Sheet: NSAnagram
--------------------------
+## Activity Sheet: NSAnagram
-First introduced in [NSHipster Quiz #4](http://nshipster.com/nshipster-quiz-4/), NSAnagram has become loved and hated, in equal parts, by those who have dared to take the challenge. Each question is an anagram, whose letters can be rearranged to form the name of a class or type in a well-known system framework (hint: Foundation, CoreFoundation, CoreLocation, StoreKit, and UIKit are represented here). Good luck!
+First introduced in [NSHipster Quiz #4](https://nshipster.com/nshipster-quiz-4/), NSAnagram has become loved and hated, in equal parts, by those who have dared to take the challenge. Each question is an anagram, whose letters can be rearranged to form the name of a class or type in a well-known system framework (hint: Foundation, CoreFoundation, CoreLocation, StoreKit, and UIKit are represented here). Good luck!
-1. Farms To Rent
-2. Zest On Mine!
-3. A Stressful Nude
-4. Non-payment Attacks, Sir!
-5. Allegiant Ace, Conglomerated
-6. Mental Burlesque Ruts
-7. Ulcer Porn: OFF
-8. Forgive Traded Crap
-9. Cautionary Mini Dam
+1. Farms To Rent
+2. Zest On Mine!
+3. A Stressful Nude
+4. Non-payment Attacks, Sir!
+5. Allegiant Ace, Conglomerated
+6. Mental Burlesque Ruts
+7. Ulcer Porn: OFF
+8. Forgive Traded Crap
+9. Cautionary Mini Dam
10. Coil Infatuation... Coil
-* * *
+---
# Answers
-Round 1: General Knowledge
---------------------------
-
-1. Heartbleed
-2. Shazam
-3. Samsung
-4. Leonardo DiCaprio, previously Christian Bale)
-5. TestFlight
-6. Dr. Dre, a.k.a Andre Romelle Young
-7. Secret
-8. Java APIs
-9. [WhatsApplebees](http://whatsapplebees.com)
+## Round 1: General Knowledge
+
+1. Heartbleed
+2. Shazam
+3. Samsung
+4. Leonardo DiCaprio, previously Christian Bale)
+5. TestFlight
+6. Dr. Dre, a.k.a Andre Romelle Young
+7. Secret
+8. Java APIs
+9. [WhatsApplebees](http://whatsapplebees.com)
10. A young Scott Forstall
-Round 2: Core Potpourri
------------------------
-
-1. [decibels (dB)](http://en.wikipedia.org/wiki/Received_signal_strength_indication)
-2. [`public.jpeg`](https://developer.apple.com/library/ios/documentation/miscellaneous/Reference/UTIRef/Articles/System-DeclaredUniformTypeIdentifiers.html)
-3. [`SecTrustEvaluate`](https://developer.apple.com/library/mac/documentation/security/Reference/certifkeytrustservices/Reference/reference.html)
-4. [`UIReferenceLibraryViewController`](http://nshipster.com/dictionary-services/)
-5. [`Address`, `AddressPair`, `Name`](https://developer.apple.com/library/mac/documentation/SystemConfiguration/Reference/SCNetworkReachabilityRef/Reference/reference.html)
-6. [`mach_timebase_info()`](https://developer.apple.com/library/ios/qa/qa1643/_index.html)
-7. [5](https://developer.apple.com/library/mac/documentation/graphicsimaging/reference/CGGeometry/Reference/reference.html#//apple_ref/c/func/CGRectDivide)
-8. [`arc4random_uniform`](https://developer.apple.com/library/mac/documentation/Darwin/Reference/Manpages/man3/arc4random_uniform.3.html)
-9. [`CFStringTransform`](https://developer.apple.com/library/mac/documentation/corefoundation/Reference/CFMutableStringRef/Reference/reference.html#//apple_ref/doc/uid/20001504-CH201-BCIGCACA)
+## Round 2: Core Potpourri
+
+1. [decibels (dB)](https://en.wikipedia.org/wiki/Received_signal_strength_indication)
+2. [`public.jpeg`](https://developer.apple.com/library/ios/documentation/miscellaneous/Reference/UTIRef/Articles/System-DeclaredUniformTypeIdentifiers.html)
+3. [`SecTrustEvaluate`](https://developer.apple.com/library/mac/documentation/security/Reference/certifkeytrustservices/Reference/reference.html)
+4. [`UIReferenceLibraryViewController`](https://nshipster.com/dictionary-services/)
+5. [`Address`, `AddressPair`, `Name`](https://developer.apple.com/library/mac/documentation/SystemConfiguration/Reference/SCNetworkReachabilityRef/Reference/reference.html)
+6. [`mach_timebase_info()`](https://developer.apple.com/library/ios/qa/qa1643/_index.html)
+7. [5](https://developer.apple.com/library/mac/documentation/graphicsimaging/reference/CGGeometry/Reference/reference.html#//apple_ref/c/func/CGRectDivide)
+8. [`arc4random_uniform`](https://developer.apple.com/library/mac/documentation/Darwin/Reference/Manpages/man3/arc4random_uniform.3.html)
+9. [`CFStringTransform`](https://developer.apple.com/library/mac/documentation/corefoundation/Reference/CFMutableStringRef/Reference/reference.html#//apple_ref/doc/uid/20001504-CH201-BCIGCACA)
10. [LLVM's Logo is a **wyvern**, or **dragon**](http://llvm.org/Logo.html). [GCC's Logo is an **egg** (with a **gnu** bursting out of it)](http://gcc.gnu.org)
-Round 3: NSAnagram
-------------------
-
-1. `NSFormatter`
-2. `NSTimeZone`
-3. `NSUserDefaults`
-4. `SKPaymentTransaction`
-5. `CLLocationManagerDelegate`
-6. `NSMutableURLRequest`
-7. `CFRunLoopRef`
-8. `CGDataProviderRef`
-9. `UIDynamicAnimator`
+## Round 3: NSAnagram
+
+1. `NSFormatter`
+2. `NSTimeZone`
+3. `NSUserDefaults`
+4. `SKPaymentTransaction`
+5. `CLLocationManagerDelegate`
+6. `NSMutableURLRequest`
+7. `CFRunLoopRef`
+8. `CGDataProviderRef`
+9. `UIDynamicAnimator`
10. `UILocalNotification`
-* * *
+---
How did you do this time? Tweet out your score to see how you stack up to your peers!
diff --git a/2014-05-19-benchmarking.md b/2014-05-19-benchmarking.md
index aa9b54bc..63ea80de 100644
--- a/2014-05-19-benchmarking.md
+++ b/2014-05-19-benchmarking.md
@@ -1,6 +1,6 @@
---
title: Benchmarking
-author: Mattt Thompson
+author: Mattt
category: Objective-C
excerpt: "Abstractions are necessary for doing meaningful work, but they come at a cost. By benchmarking, a programmer can uncover the hidden performance characteristics of their code, and use this information to optimize accordingly."
status:
@@ -33,7 +33,7 @@ The Scientific Method outlines a series of steps to logically deduce answers for
In the case of programming, there are generally two kinds of questions to be asked:
-- **What are the _absolute_ performance characteristics of this code?** Is the procedure bound by _computation_ or _memory_? What is the [limiting behavior](http://en.wikipedia.org/wiki/Big_O_notation) across different sample sizes?
+- **What are the _absolute_ performance characteristics of this code?** Is the procedure bound by _computation_ or _memory_? What is the [limiting behavior](https://en.wikipedia.org/wiki/Big_O_notation) across different sample sizes?
- **What are the _relative_ performance characteristics of this code, as compared to its alternatives?** Which is faster, methodA or methodB?
Because the underlying factors of everything from the operating system down to the metal itself are extremely variable, performance should be measured across a large number of trials. For most applications, something on the order of 105 to 108 samples should be acceptable.
@@ -44,26 +44,26 @@ For this example, let's take a look at the performance characteristics of adding
To establish a benchmark, we specify a `count` of objects to add, and the number of `iterations` to run this process.
-```objective-c
+```objc
static size_t const count = 1000;
static size_t const iterations = 10000;
```
Since we're not testing the stack allocation of objects, we declare the object to be added to the array once, outside of the benchmark.
-```objective-c
+```objc
id object = @"🐷";
```
Benchmarking is as simple as taking the time before running, and comparing it against the time after. `CACurrentMediaTime()` is a convenient way to measure time in seconds derived from `mach_absolute_time`.
-> Unlike `NSDate` or `CFAbsoluteTimeGetCurrent()` offsets, `mach_absolute_time()` and `CACurrentMediaTime()` are based on the internal host clock, a precise, monatomic measure, and not subject to changes in the external time reference, such as those caused by time zones, daylight savings, or leap seconds
+> Unlike `NSDate` or `CFAbsoluteTimeGetCurrent()` offsets, `mach_absolute_time()` and `CACurrentMediaTime()` are based on the internal host clock, a precise, monotonic measure, and not subject to changes in the external time reference, such as those caused by time zones, daylight savings, or leap seconds
`for` loops are used to increment `count` and `iterations`. Each iteration is enclosed by an `@autoreleasepool`, to keep the memory footprint low.
Putting it all together, here's a simple way to benchmark code in Objective-C:
-```objective-c
+```objc
CFTimeInterval startTime = CACurrentMediaTime();
{
for (size_t i = 0; i < iterations; i++) {
@@ -91,7 +91,7 @@ Allow me to introduce you to `dispatch_benchmark`.
`dispatch_benchmark` is part of [`libdispatch`](http://libdispatch.macosforge.org), a.k.a Grand Central Dispatch. Curiously, though, this function is not publicly declared, so you'll have to do that yourself:
-```objective-c
+```objc
extern uint64_t dispatch_benchmark(size_t count, void (^block)(void));
```
@@ -116,7 +116,7 @@ If you happened to skim all of that, be encouraged to read through that again—
Here's what the previous example looks like if we were to use `dispatch_benchmark` instead:
-```objective-c
+```objc
uint64_t t = dispatch_benchmark(iterations, ^{
@autoreleasepool {
NSMutableArray *mutableArray = [NSMutableArray array];
@@ -138,7 +138,7 @@ For this example, let's consider the age-old question of "What difference does p
Let's find out:
-```objective-c
+```objc
uint64_t t_0 = dispatch_benchmark(iterations, ^{
@autoreleasepool {
NSMutableArray *mutableArray = [NSMutableArray array];
diff --git a/2014-05-26-cocoapods.md b/2014-05-26-cocoapods.md
index 43c29a4f..a960228c 100644
--- a/2014-05-26-cocoapods.md
+++ b/2014-05-26-cocoapods.md
@@ -1,15 +1,13 @@
---
title: CocoaPods
-author: Mattt Thompson
+author: Mattt
category: Open Source
tags: cfhipsterref
excerpt: "When well thought-out and implemented, infrastructure is a multiplying force that drives growth and development. In the case of Objective-C, CocoaPods has provided a much-needed tool for channeling and organizing open source participation."
status:
- swift: n/a
+ swift: n/a
---
-
-
Civilization is built on infrastructure: roads, bridges, canals, sewers, pipes, wires, fiber. When well thought-out and implemented, infrastructure is a multiplying force that drives growth and development. But when such formative structures are absent or ad hoc, it feels as if progress is made _in spite of_ the situation.
It all has to do with solving the problem of scale.
@@ -20,10 +18,6 @@ In the case of Objective-C, [CocoaPods](http://cocoapods.org) provided a much-ne
This week on NSHipster, we'll celebrate the launch of CocoaPods 0.33, [an important milestone for the project](http://blog.cocoapods.org/CocoaPods-0.33/), by taking a look back at where we came from, discussing where we are now, and thinking about what's to come.
-> The following historical look at the origins of CocoaPods is, admittedly, a bit wordy for this publication. So if you're looking for technical details, feel free to [skip directly to that](#using-cocoapods).
-
----
-
## A Look Back
For the first twenty or so years of its existence, Objective-C was not a widely known language. NeXT and later OS X were marginal platforms, with a comparatively small user base and developer community. Like any community, there were local user groups and mailing lists and websites, but open source collaboration was not a widespread phenomenon. Granted, Open Source was only just starting to pick up steam at that time, but there was no contemporary Objective-C equivalent to, for example, CPAN, the Comprehensive Perl Archive Network. Everyone took SDKs from Redwood City and Cupertino as far as they could, (maybe sprinkling in some code salvaged from a forum thread), but ultimately rolling their own solutions to pretty much everything else.
@@ -38,13 +32,15 @@ In those early years of iPhone OS, we started to see the first massively adopted
Of this new wave of developers, those coming from a Ruby background had a significant influence on the code and culture of Objective-C. Ruby, a spiritual successor to Perl, had its own package manager similar to CPAN: [RubyGems](https://rubygems.org).
-> Why so much influence from Ruby? Here's my pet theory: Ruby started gaining popular traction because of [Rails](http://rubyonrails.org), which hit 1.0 at the end of 2005. Given that the average duration of a startup gig seems to be about 1½ – 2½ years, the timing works out such that those first and second waves of bored Rails developers itching to jump ship would find a place in the emerging app space.
+{% info %}
+Why so much influence from Ruby? Here's my pet theory: Ruby started gaining popular traction because of [Rails](http://rubyonrails.org), which hit 1.0 at the end of 2005. Given that the average duration of a startup gig seems to be about 1½ – 2½ years, the timing works out such that those first and second waves of bored Rails developers itching to jump ship would find a place in the emerging app space.
+{% endinfo %}
As open source contributions in Objective-C began to get some traction, the pain points of code distribution were starting to become pretty obvious:
Lacking frameworks, code for iOS could be packaged as a static library, but getting that set up and keeping code and static distributions in sync was an arduous process.
-Another approach was to use Git submodules, and include the source directly in the project. But getting everything working, with linked frameworks and build flags configured, was not great either—especially at a time when the body of code was split between [ARC and non-ARC](http://en.wikipedia.org/wiki/Automatic_Reference_Counting).
+Another approach was to use Git submodules, and include the source directly in the project. But getting everything working, with linked frameworks and build flags configured, was not great either—especially at a time when the body of code was split between [ARC and non-ARC](https://en.wikipedia.org/wiki/Automatic_Reference_Counting).
### Enter CocoaPods
@@ -58,15 +54,14 @@ Since its initial proof-of-concept, the project has grown to include [14 core te
A significant portion of these prolific contributions from the open source community for Objective-C has been directly enabled and encouraged by increased ownership around tooling. Everyone involved should be commended for their hard work and dedication.
-> To break the 4th wall for a moment: Seriously, _thank you_, ladies and gentlemen of CocoaPods. You've done an amazing job. Keep up the good work!
-
----
-
## Using CocoaPods
CocoaPods is easy to get started with both as a consumer and a library author. It should only take a few minutes to get set up.
-> For the most up-to-date information on how to use CocoaPods, check out the [official guides](http://guides.cocoapods.org).
+{% info %}
+For the most up-to-date information on how to use CocoaPods,
+check out the [official guides](http://guides.cocoapods.org).
+{% endinfo %}
### Installing CocoaPods
@@ -74,13 +69,18 @@ CocoaPods is installed through RubyGems, the Ruby package manager, which comes w
To install, open Terminal.app and enter the following command:
-~~~{bash}
+```terminal
$ sudo gem install cocoapods
-~~~
+```
Now you should have the `pod` command available in the terminal.
-> If you're using a Ruby versioning manager, like [rbenv](https://github.com/sstephenson/rbenv), you may need to run a command to re-link a binary shim to the library (e.g. `$ rbenv rehash`).
+{% warning %}
+If you're using a Ruby versioning manager like
+[rbenv](https://github.com/sstephenson/rbenv),
+you may need to run a command to re-link a binary shim to the library
+(e.g. `$ rbenv rehash`).
+{% endwarning %}
### Managing Dependencies
@@ -94,51 +94,53 @@ A `Podfile` is where the dependencies of a project are listed. It is equivalent
To create a Podfile, `cd` into the directory of your `.xcodeproj` file and enter the command:
-~~~{bash}
+```terminal
$ pod init
-~~~
+```
#### Podfile
-~~~{ruby}
+```ruby
platform :ios, '7.0'
target "AppName" do
end
-~~~
+```
Dependencies can have varying levels of specificity. For most libraries, pegging to a minor or patch version is the safest and easiest way to include them in your project.
-~~~{ruby}
+```ruby
pod 'X', '~> 1.1'
-~~~
+```
-> CocoaPods follows [Semantic Versioning](http://semver.org) conventions.
+{% info %}
+CocoaPods follows [Semantic Versioning](http://semver.org) conventions.
+{% endinfo %}
To include a library not included in the public specs database, a Git, Mercurial, or SVN repository can be used instead, for which a `commit`, `branch`, or `tag` can be specified.
-~~~{ruby}
+```ruby
pod 'Y', :git => 'https://github.com/NSHipster/Y.git', :commit => 'b4dc0ffee'
-~~~
+```
Once all of the dependencies have been specified, they can be installed with:
-~~~{bash}
+```terminal
$ pod install
-~~~
+```
When this is run, CocoaPods will recursively analyze the dependencies of each project, resolving them into a dependency graph, and serializing into a `Podfile.lock` file.
-> For example, if two libraries require [AFNetworking](http://afnetworking.com), CocoaPods will determine a version that satisfies both requirements, and links them with a common installation of it.
+For example, if two libraries require [AFNetworking](http://afnetworking.com), CocoaPods will determine a version that satisfies both requirements, and links them with a common installation of it.
CocoaPods will create a new Xcode project that creates static library targets for each dependency, and then links them all together into a `libPods.a` target. This static library becomes a dependency for your original application target. An `xcworkspace` file is created, and should be used from that point onward. This allows the original `xcodeproj` file to remain unchanged.
Subsequent invocations of `pod install` will add new pods or remove old pods according to the locked dependency graph. To update the individual dependencies of a project to the latest version, do the following:
-~~~{bash}
+```terminal
$ pod update
-~~~
+```
### Trying Out a CocoaPod
@@ -146,11 +148,11 @@ One great, but lesser-known, feature of CocoaPods is the `try` command, which al
Invoking `$ pod try` with the name of a project in the public specs database opens up any example projects for the library:
-~~~{bash}
+```terminal
$ pod try Ono
-~~~
+```
-
+
## Creating a CocoaPod
@@ -164,39 +166,42 @@ Remember: **_raising_ the bar for contribution within a software ecosystem _lowe
A `.podspec` file is the atomic unit of a CocoaPods dependency. It specifies the name, version, license, and source files for a library, along with other metadata.
-> The [official guide to the Podfile](http://guides.cocoapods.org/using/the-podfile) has some great information and examples.
+{% info %}
+The [official guide to the Podfile](http://guides.cocoapods.org/using/the-podfile)
+has some great information and examples.
+{% endinfo %}
#### NSHipsterKit.podspec
-~~~{ruby}
+```ruby
Pod::Spec.new do |s|
s.name = 'NSHipsterKit'
s.version = '1.0.0'
s.license = 'MIT'
s.summary = "A pretty obscure library.
You've probably never heard of it."
- s.homepage = 'http://nshipster.com'
- s.authors = { 'Mattt Thompson' =>
+ s.homepage = 'https://nshipster.com'
+ s.authors = { 'Mattt' =>
'mattt@nshipster.com' }
s.social_media_url = "https://twitter.com/mattt"
s.source = { :git => 'https://github.com/nshipster/NSHipsterKit.git', :tag => '1.0.0' }
s.source_files = 'NSHipsterKit'
end
-~~~
+```
Once published to the public specs database, anyone could add it to their project, specifying their Podfile thusly:
#### Podfile
-~~~{ruby}
+```ruby
pod 'NSHipsterKit', '~> 1.0'
-~~~
+```
A `.podspec` file can be useful for organizing internal or private dependencies as well:
-~~~{ruby}
+```ruby
pod 'Z', :path => 'path/to/directory/with/podspec'
-~~~
+```
### Publishing a CocoaPod
@@ -208,26 +213,26 @@ The CocoaPods Trunk service solves a lot of this, making the process nicer for e
To get started, you must first register your machine with the Trunk service. This is easy enough, just specify your email address (the one you use for committing library code) along with your name.
-~~~{bash}
+```terminal
$ pod trunk register mattt@nshipster.com "Mattt Thompson"
-~~~
+```
Now, all it takes to publish your code to CocoaPods is a single command. The same command works for creating a new library or adding a new version to an existing one:
-~~~{bash}
+```terminal
$ pod trunk push NAME.podspec
-~~~
+```
-> Authors of existing CocoaPods can claim their libraries [with a few simple steps](http://blog.cocoapods.org/Claim-Your-Pods/).
+{% info %}
+Authors of existing CocoaPods can claim their libraries [with a few simple steps](http://blog.cocoapods.org/Claim-Your-Pods/).
+{% endinfo %}
-***
-
-## A Look Forward
+---
CocoaPods exemplifies the compounding effect of infrastructure on a community. In a few short years, the Objective-C community has turned into something that we can feel proud to be part of.
CocoaPods is just one example of the great work being done on Objective-C infrastructure. Other community tools, like [Travis CI](http://blog.travis-ci.com/introducing-mac-ios-rubymotion-testing/), [CocoaDocs](http://cocoadocs.org), and [Nomad](http://nomad-cli.com) have dramatically improved the everyday experience iOS and OS X development for the community.
-It can be tempting to be snarky, contrarian, or grumpy about the direction of a community. No matter what, though, let us all try our best to enter into dialogue in good faith, offering constructive criticism where we can. We should help each other to be good [stewards](http://nshipster.com/stewardship/) of what we share, and strive towards [empathy](http://nshipster.com/empathy/) in all our interactions.
+It can be tempting to be snarky, contrarian, or grumpy about the direction of a community. No matter what, though, let us all try our best to enter into dialogue in good faith, offering constructive criticism where we can. We should help each other to be good [stewards](https://nshipster.com/stewardship/) of what we share, and strive towards [empathy](https://nshipster.com/empathy/) in all our interactions.
CocoaPods is a good thing for Objective-C. And it's only getting better.
diff --git a/2014-06-03-nshipster-quiz-6.md b/2014-06-03-nshipster-quiz-6.md
index 6704fb34..3a88cc9b 100644
--- a/2014-06-03-nshipster-quiz-6.md
+++ b/2014-06-03-nshipster-quiz-6.md
@@ -1,6 +1,6 @@
---
title: "NSHipster Quiz #6"
-author: Mattt Thompson
+author: Mattt
category: Trivia
excerpt: "Our second annual WWDC Pub Quiz! With dozens of teams, comprised of developers from all around the world, the competition was fierce. How will you stack up?"
status:
@@ -19,44 +19,41 @@ For everyone that couldn't make it to the event, here's an opportunity to play a
- Play with up to 5 friends for maximum enjoyment
- Don't be lame and look things up on the Internet or in Xcode
-* * *
+---
-Round 1: General Knowledge
---------------------------
+## Round 1: General Knowledge
Current events, miscellaneous tidbits, and random trivia. Following a time-honored traditions for NSHipster quizzes, the first round is always a mis-mash of people, places, and pop culture.
-1. On iOS 8, what magic phrase can be used to activate Siri, when the device is plugged in?
-2. What game, crestfallen by the runaway success of its clone, 2048, was at least slightly vindicated last night with an ADA win?
-3. Which alternative search engine was added to the latest release of Safari?
-4. Weeks after its announcement, Apple finally confirmed its $3B acquisition of Beats Electronics. What is the name of Dre’s Co-founder?
-5. Yosemite is, of course, the code name of OS X Yosemite, but this code name was used before. What was the product? _(Hint: It was released in 1999 and had a top clock speed of 450MHz)_
-6. What is the name of the valley in Yosemite that was flooded after construction of the O'Shaughnessy Dam in 1927, which provides drinking water to San Francisco?
-7. Much of the reason why Yosemite exists today is thanks to the Sierra Club and a Scottish-born naturalist. What is this gentleman's name?
-8. 20 years ago, Apple launched a new experimental language. It had a syntax like this: `let x :: = 2;`. What was this language’s name?
-9. What does a Swift eat?
+1. On iOS 8, what magic phrase can be used to activate Siri, when the device is plugged in?
+2. What game, crestfallen by the runaway success of its clone, 2048, was at least slightly vindicated last night with an ADA win?
+3. Which alternative search engine was added to the latest release of Safari?
+4. Weeks after its announcement, Apple finally confirmed its $3B acquisition of Beats Electronics. What is the name of Dre’s Co-founder?
+5. Yosemite is, of course, the code name of OS X Yosemite, but this code name was used before. What was the product? _(Hint: It was released in 1999 and had a top clock speed of 450MHz)_
+6. What is the name of the valley in Yosemite that was flooded after construction of the O'Shaughnessy Dam in 1927, which provides drinking water to San Francisco?
+7. Much of the reason why Yosemite exists today is thanks to the Sierra Club and a Scottish-born naturalist. What is this gentleman's name?
+8. 20 years ago, Apple launched a new experimental language. It had a syntax like this: `let x :: = 2;`. What was this language’s name?
+9. What does a Swift eat?
10. What is the birdwatching term for the overall impression or appearance of a bird based on its shape, posture, & flying style?
-Round 2: So You Think You Can Swift?
-------------------------------------
+## Round 2: So You Think You Can Swift?
Having only been introduced the day before, Swift was fresh on everyone's minds, and the hot topic of conversation. To put everyone's knowledge to the test, the following 10 exercises were posed to attendees.
For anyone revisiting this quiz months or years after the fact, this should be incredibly easy. But just imagine coming into these questions having only skimmed a few hundred pages of the Swift iBook (if at all). And now imagine that you're a beer and a half into a late night during the week of WWDC. Now you can start to appreciate how grumpy this round made a lot of people.
-1. Declare a constant `d` equal to `3.0`.
-2. Declare a variable `s` of type `String`.
-3. Interpolate the value of `d` into a String literal.
-4. Set a var `b` to `28` using an octal literal.
-5. Declare an optional property of type `Int` named `x`.
-6. Declare a Highlander `enum` of type `Int`, with an element named "One".
-7. Override viewDidLoad in a UIViewController Subclass.
-8. Declare a class C that adopting the NSCoding protocol.
-9. Alias `String` as `Rope`.
+1. Declare a constant `d` equal to `3.0`.
+2. Declare a variable `s` of type `String`.
+3. Interpolate the value of `d` into a String literal.
+4. Set a var `b` to `28` using an octal literal.
+5. Declare an optional property of type `Int` named `x`.
+6. Declare a Highlander `enum` of type `Int`, with an element named "One".
+7. Override viewDidLoad in a UIViewController Subclass.
+8. Declare a class C that adopting the NSCoding protocol.
+9. Alias `String` as `Rope`.
10. Declare a protocol method `m`, which returns both an `Int`, and a `Dictionary`, with `String` keys and any value.
-Round 3: Music Round
---------------------
+## Round 3: Music Round
Rounding out the competition was the venerable staple of pub trivia everywhere: the music round!
@@ -65,64 +62,61 @@ The theme of this music round is—you guessed it—songs used in Apple commerci
> Due to the peculiarities of embedded video, you may have to click through in order to hear the song. Don't worry—the link is to the song itself, not the Apple ad, so you'll at least have something to puzzle over.
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-* * *
+---
# Answers
-Round 1: General Knowledge
---------------------------
-
-1. "Hey Siri"
-2. [Threes](http://asherv.com/threes/)
-3. [DuckDuckGo](https://duckduckgo.com)
-4. [Jimmy Iovine](http://en.wikipedia.org/wiki/Jimmy_Iovine)
-5. [Power Macintosh G3 (Blue & White)](http://en.wikipedia.org/wiki/Power_Macintosh_G3_%28Blue_%26_White%29)
-6. [Hetch Hetchy](http://en.wikipedia.org/wiki/Hetch_Hetchy)
-7. [John Muir](http://en.wikipedia.org/wiki/John_Muir)
-8. [Dylan](http://en.wikipedia.org/wiki/Dylan_(programming_language))
-9. [Insects](http://en.wikipedia.org/wiki/Swift#Feeding)
-10. [Jizz](http://en.wikipedia.org/wiki/Jizz_%28birding%29)
-
-Round 2: So You Think You Can Swift?
-------------------------------------
-
-1. `let d = 3.0`
-2. `var s: String`
-3. `"\(d)"`
-4. `var b = 0o34`
-5. `var x: Int?`
-6. `enum Highlander: Int { case One = 1}`
-7. `override func viewDidLoad() { ... }`
-8. `class C: NSCoding`
-9. `typealias Rope = String`
+## Round 1: General Knowledge
+
+1. "Hey Siri"
+2. [Threes](http://asherv.com/threes/)
+3. [DuckDuckGo](https://duckduckgo.com)
+4. [Jimmy Iovine](https://en.wikipedia.org/wiki/Jimmy_Iovine)
+5. [Power Macintosh G3 (Blue & White)](https://en.wikipedia.org/wiki/Power_Macintosh_G3_%28Blue_%26_White%29)
+6. [Hetch Hetchy](https://en.wikipedia.org/wiki/Hetch_Hetchy)
+7. [John Muir](https://en.wikipedia.org/wiki/John_Muir)
+8. [Dylan]()
+9. [Insects](https://en.wikipedia.org/wiki/Swift#Feeding)
+10. [Jizz](https://en.wikipedia.org/wiki/Jizz_%28birding%29)
+
+## Round 2: So You Think You Can Swift?
+
+1. `let d = 3.0`
+2. `var s: String`
+3. `"\(d)"`
+4. `var b = 0o34`
+5. `var x: Int?`
+6. `enum Highlander: Int { case One = 1}`
+7. `override func viewDidLoad() { ... }`
+8. `class C: NSCoding`
+9. `typealias Rope = String`
10. `func m() -> (Int, Dictionary)`
-Round 3: Music Round
---------------------
-
-1. [**iPod 3G**: Jet — "Are You Gonna Be My Girl?"](https://www.youtube.com/watch?v=TaVFCdwT0hk)
-2. [**G4 Cube**: The Jimi Hendrix Experience — "Purple Haze"](https://www.youtube.com/watch?v=rzn0dhNm0aE)
-3. [**iPod Shuffle**: Caesars — "Jerk It Out"](https://www.youtube.com/watch?v=nuLx1Uk1ceg)
-4. [**iTunes**: Green Day — "I Fought The Law"](https://www.youtube.com/watch?v=rcToyN2_cSs)
-5. [**iPod 4G (U2 Special Edition)**: U2 — "Vertigo"](https://www.youtube.com/watch?v=t1ENjxFMXkw)
-6. [**OS X (First-Run Experience)**: Honeycut — "Exodus Honey"](https://www.youtube.com/watch?v=hmb1t8RMsu4)
-7. [**iPod 1G**: The Propellerheads — "Take California"](https://www.youtube.com/watch?v=eb1bPg4NlwY)
-8. [**iPhone 5s**: Pixies — "Gigantic"](https://www.youtube.com/watch?v=ODmfmUWqlSA)
-9. [**iBook**: Miles Davis — "Flamenco Sketches" (with voiceover by Jeff Goldblum)](https://www.youtube.com/watch?v=T3Gvy-8gtOU)
+## Round 3: Music Round
+
+1. [**iPod 3G**: Jet — "Are You Gonna Be My Girl?"](https://www.youtube.com/watch?v=TaVFCdwT0hk)
+2. [**G4 Cube**: The Jimi Hendrix Experience — "Purple Haze"](https://www.youtube.com/watch?v=rzn0dhNm0aE)
+3. [**iPod Shuffle**: Caesars — "Jerk It Out"](https://www.youtube.com/watch?v=nuLx1Uk1ceg)
+4. [**iTunes**: Green Day — "I Fought The Law"](https://www.youtube.com/watch?v=rcToyN2_cSs)
+5. [**iPod 4G (U2 Special Edition)**: U2 — "Vertigo"](https://www.youtube.com/watch?v=t1ENjxFMXkw)
+6. [**OS X (First-Run Experience)**: Honeycut — "Exodus Honey"](https://www.youtube.com/watch?v=hmb1t8RMsu4)
+7. [**iPod 1G**: The Propellerheads — "Take California"](https://www.youtube.com/watch?v=eb1bPg4NlwY)
+8. [**iPhone 5s**: Pixies — "Gigantic"](https://www.youtube.com/watch?v=ODmfmUWqlSA)
+9. [**iBook**: Miles Davis — "Flamenco Sketches" (with voiceover by Jeff Goldblum)](https://www.youtube.com/watch?v=T3Gvy-8gtOU)
10. [**iMac "Sage"**: Kermit The Frog — "(It’s Not Easy) Bein' Green"](https://www.youtube.com/watch?v=0awG75V2OQw)
-* * *
+---
How did you do this time? Tweet out your score to see how you stack up to your peers!
diff --git a/2014-06-09-ios8.md b/2014-06-09-ios8.md
index 1b670635..44046079 100644
--- a/2014-06-09-ios8.md
+++ b/2014-06-09-ios8.md
@@ -1,7 +1,7 @@
---
title: iOS 8
-author: Mattt Thompson
-category: ""
+author: Mattt
+category: Miscellaneous
excerpt: "Ask anyone, and they'll tell you: WWDC 2014 was the one of the most exciting in recent memory. This week, we'll take a look beneath the headline features, and share some of the more obscure APIs that everyone should know about."
status:
swift: 2.0
@@ -26,12 +26,12 @@ This week, we'll take a look beneath the headline features, and share some of th
Forget `[[UIDevice currentDevice] systemVersion]` and `NSFoundationVersionNumber`, there's a new way to determine the current operating system in code: `NSProcessInfo -isOperatingSystemAtLeastVersion`
-~~~{swift}
+```swift
import Foundation
let yosemite = NSOperatingSystemVersion(majorVersion: 10, minorVersion: 10, patchVersion: 0)
NSProcessInfo().isOperatingSystemAtLeastVersion(yosemite) // false
-~~~
+```
Keep in mind, however, that a test for capability, such as with `SomeClass.class` or `respondsToSelector:`, is preferable to checking the OS version. Compiler macros in C or Swift can be used to conditionally compile source based on the build configuration of the target.
@@ -39,7 +39,7 @@ Keep in mind, however, that a test for capability, such as with `SomeClass.class
One of the features most sorely lacking in Foundation was the ability to work with units for quantities like mass or length. In iOS 8 and OS X Yosemite, three new classes were introduced that fills the gap: `NSEnergyFormatter`, `NSMassFormatter`, & `NSLengthFormatter`.
-> This effectively doubles the number of [`NSFormatter`](http://nshipster.com/nsformatter/) subclasses in Foundation, which was previously limited to `NSNumberFormatter`, `NSDateFormatter`, & `NSByteCountFormatter`.
+> This effectively doubles the number of [`NSFormatter`](https://nshipster.com/nsformatter/) subclasses in Foundation, which was previously limited to `NSNumberFormatter`, `NSDateFormatter`, & `NSByteCountFormatter`.
Although these new formatter classes are part of Foundation, they were added primarily for use in HealthKit.
@@ -47,33 +47,33 @@ Although these new formatter classes are part of Foundation, they were added pri
`NSEnergyFormatter` formats energy in Joules, the raw unit of work for exercises, and Calories, which is used when working with nutrition information.
-~~~{swift}
+```swift
let energyFormatter = NSEnergyFormatter()
energyFormatter.forFoodEnergyUse = true
let joules = 10_000.0
print(energyFormatter.stringFromJoules(joules)) // "2.39 Cal"
-~~~
+```
### NSMassFormatter
Although the fundamental unit of physical existence, mass is pretty much relegated to tracking the weight of users in HealthKit. Yes, mass and weight are different, but this is programming, not science class, so stop being pedantic.
-~~~{swift}
+```swift
let massFormatter = NSMassFormatter()
let kilograms = 60.0
print(massFormatter.stringFromKilograms(kilograms)) // "132 lb"
-~~~
+```
### NSLengthFormatter
Rounding out the new `NSFormatter` subclasses is `NSLengthFormatter`. Think of it as a more useful version of `MKDistanceFormatter`, with more unit options and formatting options.
-~~~{swift}
+```swift
let lengthFormatter = NSLengthFormatter()
let meters = 5_000.0
print(lengthFormatter.stringFromMeters(meters)) // "3.107 mi"
-~~~
+```
## CMPedometer
@@ -81,7 +81,7 @@ Continuing on iOS 8's health kick, `CMStepCounter` is revamped in the latest rel
It's amazing what that M7 chip is capable of.
-~~~{swift}
+```swift
import CoreMotion
let lengthFormatter = NSLengthFormatter()
@@ -99,13 +99,13 @@ pedometer.startPedometerUpdatesFromDate(NSDate()) { data, error in
}
}
}
-~~~
+```
## CMAltimeter
On supported devices, a `CMPedometer`'s stats on `floorsAscended` / `floorsDescended` can be augmented with `CMAltimeter` to get a more granular look at vertical distance traveled:
-~~~{swift}
+```swift
import CoreMotion
let altimeter = CMAltimeter()
@@ -116,13 +116,13 @@ if CMAltimeter.isRelativeAltitudeAvailable() {
}
}
}
-~~~
+```
## CLFloor
`CLFloor` is a new API in iOS 8 that ties the new features in CoreMotion with Apple's ambitious plan to map the interiors of the largest buildings in the world. Look for this information to play a significant role in future hyperlocal mapping applications.
-~~~{swift}
+```swift
import CoreLocation
class LocationManagerDelegate: NSObject, CLLocationManagerDelegate {
@@ -136,7 +136,7 @@ class LocationManagerDelegate: NSObject, CLLocationManagerDelegate {
let manager = CLLocationManager()
manager.delegate = LocationManagerDelegate()
manager.startUpdatingLocation()
-~~~
+```
## HKStatistics
@@ -146,7 +146,7 @@ HealthKit manages your biometrics from all of your devices in a single unified A
The following example shows how statistics summed over the duration of the day can be grouped and interpreted individually:
-~~~{swift}
+```swift
import HealthKit
let collection: HKStatisticsCollection? = ...
@@ -174,7 +174,7 @@ for source in sources {
}
}
}
-~~~
+```
NSHipster will be covering a lot more about HealthKit in future editions, so stay tuned!
@@ -182,7 +182,7 @@ NSHipster will be covering a lot more about HealthKit in future editions, so sta
In many ways, WWDC 2014 was the year that Apple _fixed their shit_. Small things, like adding the missing `NSStream` initializer for creating a bound stream pair (without resorting to awkwardly-bridged `CFStreamCreatePairWithSocketToHost` call). Behold: `+[NSStream getStreamsToHostWithName:port:inputStream:outputStream:]`
-~~~{swift}
+```swift
var inputStream: NSInputStream?
var outputStream: NSOutputStream?
@@ -190,18 +190,18 @@ NSStream.getStreamsToHostWithName("nshipster.com",
port: 5432,
inputStream: &inputStream,
outputStream: &outputStream)
-~~~
+```
## NSString -localizedCaseInsensitiveContainsString
Also filed under: "small but solid fixes", is this convenience method for `String`/`NSString`:
-~~~{swift}
+```swift
let string = "Café"
let substring = "É"
string.localizedCaseInsensitiveContainsString(substring) // true
-~~~
+```
## CTRubyAnnotationRef
@@ -209,7 +209,7 @@ If you're a linguistics and typography nerd, this new addition to the CoreText f
...oh right. Ruby. No, not [Ruby](https://www.ruby-lang.org/en/). [Ruby](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ruby). It's used to display the pronunciation of characters in certain Asian scripts.
-~~~{objective-c}
+```objc
@import CoreText;
NSString *kanji = @"猫";
@@ -220,7 +220,7 @@ CFStringRef furigana[kCTRubyPositionCount] =
CTRubyAnnotationRef ruby =
CTRubyAnnotationCreate(kCTRubyAlignmentAuto, kCTRubyOverhangAuto, 0.5, furigana);
-~~~
+```
Admittedly, the documentation isn't entirely clear on how exactly to incorporate this into the rest of your `CoreText` drawing calls, but the result would look something like this:
@@ -242,30 +242,30 @@ What's even nerdier than Ruby annotations? The new calendar identifiers added to
The Foundation URL Loading System has remained relatively unchanged since last year's `NSURLSession` blowout. However, `NSURLCredentialStorage` has been given some TLC, with new functions that get and set credentials for tasks in asynchronous, non-blocking fashion.
-~~~{swift}
+```swift
import Foundation
let session = NSURLSession()
-let task = session.dataTaskWithURL(NSURL(string: "http://nshipster.com")!) { data, response, error in
- // ...
+let task = session.dataTaskWithURL(NSURL(string: "https://nshipster.com")!) { data, response, error in
+ <#...#>
}
let protectionSpace = NSURLProtectionSpace()
let credentialStorage = NSURLCredentialStorage()
credentialStorage.getCredentialsForProtectionSpace(protectionSpace, task: task) { credentials in
- // ...
+ <#...#>
}
-~~~
+```
## kUTTypeToDoItem
-Looking through the latest API diffs, one might notice the large number of new [UTIs](http://en.wikipedia.org/wiki/Uniform_Type_Identifier) constants. One that caught my eye was `kUTTypeToDoItem`:
+Looking through the latest API diffs, one might notice the large number of new [UTIs](https://en.wikipedia.org/wiki/Uniform_Type_Identifier) constants. One that caught my eye was `kUTTypeToDoItem`:
-~~~{swift}
+```swift
import MobileCoreServices
kUTTypeToDoItem // "public.to-do-item"
-~~~
+```
As a public type, iOS & OS X now provide a unified way to share tasks between applications. If you happen to work on a task management tool (and, let's be honest, the chances are extremely good, considering how damn many of them there are in the App Store), proper integration with this system type should be put at the top of your list.
@@ -275,7 +275,7 @@ Most users are completely unaware that most pictures taken with phones these day
New to the Image I/O framework is a convenient new option for `CGImageDestination`: `kCGImageMetadataShouldExcludeGPS`, which does what you'd expect.
-~~~{swift}
+```swift
import UIKit
import ImageIO
import MobileCoreServices
@@ -291,8 +291,8 @@ if let imageDestination = CGImageDestinationCreateWithURL(fileURL, kUTTypeJPEG,
CGImageDestinationAddImage(imageDestination, cgImage, options)
CGImageDestinationFinalize(imageDestination)
}
-~~~
-~~~{objective-c}
+```
+```objc
@import UIKit;
@import ImageIO;
@import MobileCoreServices;
@@ -314,7 +314,7 @@ CGImageDestinationCreateWithURL((__bridge CFURLRef)fileURL,
CGImageDestinationAddImage(imageDestinationRef, [image CGImage], (__bridge CFDictionaryRef)options);
CGImageDestinationFinalize(imageDestinationRef);
CFRelease(imageDestinationRef);
-~~~
+```
## WTF_PLATFORM_IOS
@@ -326,7 +326,7 @@ CFRelease(imageDestinationRef);
`WKWebView` offers Safari-level performance to your own app, and further improves on `UIWebView` with preferences and configurations:
-~~~{swift}
+```swift
import WebKit
let preferences = WKPreferences()
@@ -336,9 +336,9 @@ let configuration = WKWebViewConfiguration()
configuration.preferences = preferences
let webView = WKWebView(frame: self.view.bounds, configuration: configuration)
-let request = NSURLRequest(URL: NSURL(string: "http://nshipster.com")!)
+let request = NSURLRequest(URL: NSURL(string: "https://nshipster.com")!)
webView.loadRequest(request)
-~~~
+```
## NSQualityOfService
@@ -363,14 +363,14 @@ Imagine: with CloudKit and LocalAuthentication, nearly all of the friction to cr
LocalAuthentication works in terms of an `LAContext` class, which evaluates a specified policy, and gives a thumbs up or thumbs down on user authentication. At no point is any biometric information made available to the application—everything is kept safe on the hardware itself.
-~~~{swift}
+```swift
let context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.DeviceOwnerAuthenticationWithBiometrics, error: &error) {
context.evaluatePolicy(.DeviceOwnerAuthenticationWithBiometrics, localizedReason: "...") { success, error in
if success {
- // ...
+ <#...#>
} else {
print("Error: \(error)")
}
@@ -378,8 +378,8 @@ if context.canEvaluatePolicy(.DeviceOwnerAuthenticationWithBiometrics, error: &e
} else {
print("Error: \(error)")
}
-~~~
-~~~{objective-c}
+```
+```objc
LAContext *context = [[LAContext alloc] init];
NSError *error = nil;
@@ -390,7 +390,7 @@ if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:NSLocalizedString(@"...", nil)
reply:^(BOOL success, NSError *error) {
if (success) {
- // ...
+ <#...#>
} else {
NSLog(@"%@", error);
}
@@ -398,7 +398,7 @@ if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
} else {
NSLog(@"%@", error);
}
-~~~
+```
* * *
diff --git a/2014-06-16-clang-diagnostics.md b/2014-06-16-clang-diagnostics.md
index 6533ac52..b049953b 100644
--- a/2014-06-16-clang-diagnostics.md
+++ b/2014-06-16-clang-diagnostics.md
@@ -1,10 +1,11 @@
---
title: Clang Diagnostics
-author: Mattt Thompson
+author: Mattt
category: Objective-C
excerpt: "Diagnostics combine logic with analytics to arrive at a conclusion. It's science and engineering at their purest. It's human reasoning at its most potent. For us developers, our medium of code informs the production of subsequent code, creating a positive feedback loop that has catapulted the development of technology exponentially over the last half century. For us Objective-C developers specifically, the most effective diagnostics come from Clang."
status:
swift: n/a
+retired: true
---
Diagnostics combine logic with analytics to arrive at a conclusion. It's science and engineering at their purest. It's human reasoning at its most potent.
@@ -17,29 +18,29 @@ Clang is the C / Objective-C front-end to the LLVM compiler. It has a deep under
That amazing readout you get when you "Build & Analyze" (`⌘⇧B`) is a function of the softer, more contemplative side of Clang: its code diagnostics.
-In our article about [`#pragma`](http://nshipster.com/pragma/), we quipped:
+In our article about [`#pragma`](https://nshipster.com/pragma/), we quipped:
> Pro tip: Try setting the `-Weverything` flag and checking the "Treat Warnings as Errors" box your build settings. This turns on Hard Mode in Xcode.
Now, we stand by this advice, and encourage other developers to step up their game and treat build warnings more seriously. However, there are some situations in which you and Clang reach an impasse. For example, consider the following `switch` statement:
-~~~{objective-c}
+```objc
switch (style) {
case UITableViewCellStyleDefault:
case UITableViewCellStyleValue1:
case UITableViewCellStyleValue2:
case UITableViewCellStyleSubtitle:
- // ...
+ <#...#>
default:
return;
}
-~~~
+```
When certain flags are enabled, Clang will complain that the "default label in switch which covers all enumeration values". However, if we _know_ that, zooming out into a larger context, `style` is (for better or worse) derived from an external representation (e.g. JSON resource) that allows for unconstrained `NSInteger` values, the `default` case is a necessary safeguard. The only way to insist on this inevitability is to use `#pragma` to ignore a warning flag temporarily:
> `push` & `pop` are used to save and restore the compiler state, similar to Core Graphics or OpenGL contexts.
-~~~{objective-c}
+```objc
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wcovered-switch-default"
switch (style) {
@@ -47,21 +48,17 @@ switch (style) {
case UITableViewCellStyleValue1:
case UITableViewCellStyleValue2:
case UITableViewCellStyleSubtitle:
- // ...
+ <#...#>
default:
return;
}
#pragma clang diagnostic pop
-~~~
+```
> Again, and this cannot be stressed enough, Clang is right at least 99% of the time. Actually fixing an analyzer warning is _strongly_ preferred to ignoring it. Use `#pragma clang diagnostic ignored` as a method of last resort.
This week, as a public service, we've compiled a (mostly) comprehensive list of Clang warning strings and their associated flags, which can be found here:
-**[F\*\*\*ingClangWarnings.com](http://fuckingclangwarnings.com)**
+**[ClangWarnings.com](https://clangwarnings.com)**
You can also find the compiler and analyzer flags for any warning you might encounter by `^`-Clicking the corresponding entry in the Xcode Issue Navigator and selecting "Reveal in Log". (If this option is disabled, try building the project again).
-
-* * *
-
-Corrections? Additions? Open a [Pull Request](https://github.com/mattt/fuckingclangwarnings.com/pulls) to submit your change. Any help would be greatly appreciated.
diff --git a/2014-07-07-nscalendarunityear.md b/2014-07-07-nscalendarunityear.md
index 4d07c3ca..74786f33 100644
--- a/2014-07-07-nscalendarunityear.md
+++ b/2014-07-07-nscalendarunityear.md
@@ -1,33 +1,34 @@
---
title: NSCalendarUnitYear
-author: Mattt Thompson
+author: Mattt
category: Swift
excerpt: "NSHipster.com was launched 2 years ago to the day. Each week since has featured a new article on some obscure topic in Objective-C or Cocoa (with only a couple gaps). Let's celebrate with some cake."
+retired: true
status:
- swift: 2.0
- reviewed: September 9, 2015
+ swift: 2.0
+ reviewed: September 9, 2015
---
-NSHipster.com was launched 2 years ago to the day, with [a little article about NSIndexSet](http://nshipster.com/nsindexset/). Each week since has featured a new article on some obscure topic in Objective-C or Cocoa (with only a couple gaps), which have been read by millions of visitors in over 180 different countries.
+NSHipster.com was launched 2 years ago to the day, with [a little article about NSIndexSet](https://nshipster.com/nsindexset/). Each week since has featured a new article on some obscure topic in Objective-C or Cocoa (with only a couple gaps), which have been read by millions of visitors in over 180 different countries.
-> This is actually the 101st article, which means that [by television industry standards](http://en.wikipedia.org/wiki/100_episodes), this site is now suitable for broadcast syndication. (Coming soon to TBS!)
+> This is actually the 101st article, which means that [by television industry standards](https://en.wikipedia.org/wiki/100_episodes), this site is now suitable for broadcast syndication. (Coming soon to TBS!)
Let's celebrate with some cake:
Cute, right? Let's see what this looks like in code:
-~~~{swift}
+```swift
var cakePath = UIBezierPath()
cakePath.moveToPoint(CGPointMake(31.5, 32.5))
cakePath.addCurveToPoint(CGPointMake(6.5, 66.1), controlPoint1: CGPointMake(31.5, 32.5), controlPoint2: CGPointMake(6.9, 46.3))
@@ -35,13 +36,13 @@ cakePath.addCurveToPoint(CGPointMake(6.5, 66.5), controlPoint1: CGPointMake(6.5,
cakePath.addLineToPoint(CGPointMake(6.5, 95))
...
-~~~
+```
Wait, hold up. What is this, Objective-C? Manipulating `UIBezierPath`s isn't exactly ground-breaking stuff, but with a few dozen more lines to go, we can make this a bit easier for ourselves.
How about we put some syntactic icing on this cake with some [custom operators](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-XID_28)?
-~~~{swift}
+```swift
infix operator ---> { associativity left }
func ---> (left: UIBezierPath, right: (CGFloat, CGFloat)) -> UIBezierPath {
let (x, y) = right
@@ -65,7 +66,7 @@ func +~ (left: UIBezierPath, right: ((CGFloat, CGFloat), (CGFloat, CGFloat), (CG
return left
}
-~~~
+```
> Get it? `--->` replaces `moveToPoint`, while `+-` replaces `addLineToPoint`, and `+~` replaces `addCurveToPoint`. This declaration also does away with all of the redundant calls to `CGPointMake`, opting instead for simple coordinate tuples.
@@ -81,7 +82,6 @@ After full Emoji support (`let 🐶🐮`), custom operators are perhaps the shin
### A Dramatization of the Perils of Shiny Swift Features
-
> `SCENE: SAN FRANCISCO, THE YEAR IS 2017`
>
> `GREYBEARD:` So I inherited an old Swift codebase today, and I found this line of code—I swear to `$DEITY`—it just reads `😾 |--~~> 💩`.
@@ -102,7 +102,7 @@ The moral of that cautionary tale: **use custom operators and emoji sparingly**.
(Or whatever, the very next code sample totally ignores that advice)
-~~~{swift}
+```swift
// Happy 2nd Birthday, NSHipster
// 😗💨🎂✨2️⃣
@@ -168,13 +168,13 @@ UIColor.whiteColor().setFill()
UIColor.blackColor().setStroke()
📍.fill()
📍.stroke()
-~~~
+```
I'm as amazed as anyone that this actually compiles.
Everything is terrible.
-* * *
+---
Anyway, Happy 2nd Birthday, NSHipster!
diff --git a/2014-07-14-nsoperation.md b/2014-07-14-nsoperation.md
index bb758e8d..8a590a0e 100644
--- a/2014-07-14-nsoperation.md
+++ b/2014-07-14-nsoperation.md
@@ -1,6 +1,6 @@
---
title: NSOperation
-author: Mattt Thompson
+author: Mattt
category: Cocoa
tags: nshipster
excerpt: "In life, there's always work to be done. Every day brings with it a steady stream of tasks and chores to fill the working hours of our existence. Productivity is, as in life as it is in programming, a matter of scheduling and prioritizing and multi-tasking work in order to keep up appearances."
@@ -15,7 +15,7 @@ Yet, no matter how burdened one's personal ToDo list becomes, it pales in compar
Productivity is, as in life as it is in programming, a matter of scheduling and prioritizing and multi-tasking work in order to keep up appearances.
-The secret to making apps snappy is to offload as much unnecessary work to the background as possible, and in this respect, the modern Cocoa developer has two options: [Grand Central Dispatch](http://en.wikipedia.org/wiki/Grand_Central_Dispatch) and [`NSOperation`](https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/NSOperation_class/Reference/Reference.html). This article will primarily focus on the latter, though it's important to note that the two are quite complementary (more on that later).
+The secret to making apps snappy is to offload as much unnecessary work to the background as possible, and in this respect, the modern Cocoa developer has two options: [Grand Central Dispatch](https://en.wikipedia.org/wiki/Grand_Central_Dispatch) and [`NSOperation`](https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/NSOperation_class/Reference/Reference.html). This article will primarily focus on the latter, though it's important to note that the two are quite complementary (more on that later).
* * *
@@ -29,7 +29,7 @@ But simply wrapping computation into an object doesn't do much without a little
## NSOperationQueue
-`NSOperationQueue` regulates the concurrent execution of operations. It acts as a priority queue, such that operations are executed in a roughly [First-In-First-Out](http://en.wikipedia.org/wiki/FIFO) manner, with higher-priority (`NSOperation.queuePriority`) ones getting to jump ahead of lower-priority ones. `NSOperationQueue` can also limit the maximum number of concurrent operations to be executed at any given moment, using the `maxConcurrentOperationCount` property.
+`NSOperationQueue` regulates the concurrent execution of operations. It acts as a priority queue, such that operations are executed in a roughly [First-In-First-Out](https://en.wikipedia.org/wiki/FIFO) manner, with higher-priority (`NSOperation.queuePriority`) ones getting to jump ahead of lower-priority ones. `NSOperationQueue` can also limit the maximum number of concurrent operations to be executed at any given moment, using the `maxConcurrentOperationCount` property.
> NSOperationQueue itself is backed by a Grand Central Dispatch queue, though that's a private implementation detail.
@@ -66,7 +66,7 @@ All operations may not be equally important. Setting the `queuePriority` propert
### NSOperationQueuePriority
-~~~{swift}
+```swift
public enum NSOperationQueuePriority : Int {
case VeryLow
case Low
@@ -74,8 +74,8 @@ public enum NSOperationQueuePriority : Int {
case High
case VeryHigh
}
-~~~
-~~~{objective-c}
+```
+```objc
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
@@ -83,7 +83,7 @@ typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
};
-~~~
+```
## Quality of Service
@@ -99,7 +99,7 @@ The following enumerated values are used to denote the nature and urgency of an
### NSQualityOfService
-~~~{swift}
+```swift
@available(iOS 8.0, OSX 10.10, *)
public enum NSQualityOfService : Int {
case UserInteractive
@@ -108,8 +108,8 @@ public enum NSQualityOfService : Int {
case Background
case Default
}
-~~~
-~~~{objective-c}
+```
+```objc
typedef NS_ENUM(NSInteger, NSQualityOfService) {
NSQualityOfServiceUserInteractive = 0x21,
NSQualityOfServiceUserInitiated = 0x19,
@@ -117,7 +117,7 @@ typedef NS_ENUM(NSInteger, NSQualityOfService) {
NSQualityOfServiceBackground = 0x09,
NSQualityOfServiceDefault = -1
} NS_ENUM_AVAILABLE(10_10, 8_0);
-~~~
+```
- `.UserInteractive`:UserInteractive QoS is used for work directly involved in providing an interactive UI such as processing events or drawing to the screen.
- `.UserInitiated`: UserInitiated QoS is used for performing work that has been explicitly requested by the user and for which results must be immediately presented in order to allow for further user interaction. For example, loading an email after a user has selected it in a message list.
@@ -125,21 +125,21 @@ typedef NS_ENUM(NSInteger, NSQualityOfService) {
- `.Background`: Background QoS is used for work that is not user initiated or visible. In general, a user is unaware that this work is even happening and it will run in the most efficient manner while giving the most deference to higher QoS work. For example, pre-fetching content, search indexing, backups, and syncing of data with external systems.
- `.Default`: Default QoS indicates the absence of QoS information. Whenever possible QoS information will be inferred from other sources. If such inference is not possible, a QoS between UserInitiated and Utility will be used.
-~~~{swift}
+```swift
let backgroundOperation = NSOperation()
backgroundOperation.queuePriority = .Low
backgroundOperation.qualityOfService = .Background
let operationQueue = NSOperationQueue.mainQueue()
operationQueue.addOperation(backgroundOperation)
-~~~
-~~~{objective-c}
+```
+```objc
NSOperation *backgroundOperation = [[NSOperation alloc] init];
backgroundOperation.queuePriority = NSOperationQueuePriorityLow;
backgroundOperation.qualityOfService = NSOperationQualityOfServiceBackground;
[[NSOperationQueue mainQueue] addOperation:backgroundOperation];
-~~~
+```
## Asynchronous Operations
@@ -155,16 +155,16 @@ For example, to describe the process of downloading and resizing an image from a
Expressed in code:
-~~~{swift}
+```swift
let networkingOperation: NSOperation = ...
let resizingOperation: NSOperation = ...
resizingOperation.addDependency(networkingOperation)
let operationQueue = NSOperationQueue.mainQueue()
operationQueue.addOperations([networkingOperation, resizingOperation], waitUntilFinished: false)
-~~~
+```
-~~~{objective-c}
+```objc
NSOperation *networkingOperation = ...
NSOperation *resizingOperation = ...
[resizingOperation addDependency:networkingOperation];
@@ -172,7 +172,7 @@ NSOperation *resizingOperation = ...
NSOperationQueue *operationQueue = [NSOperationQueue mainQueue];
[operationQueue addOperation:networkingOperation];
[operationQueue addOperation:resizingOperation];
-~~~
+```
An operation will not be started until all of its dependencies return `true` for `finished`.
@@ -182,23 +182,23 @@ Make sure not to accidentally create a dependency cycle, such that A depends on
When an `NSOperation` finishes, it will execute its `completionBlock` exactly once. This provides a really nice way to customize the behavior of an operation when used in a model or view controller.
-~~~{swift}
+```swift
let operation = NSOperation()
operation.completionBlock = {
print("Completed")
}
NSOperationQueue.mainQueue().addOperation(operation)
-~~~
+```
-~~~{objective-c}
+```objc
NSOperation *operation = ...;
operation.completionBlock = ^{
NSLog("Completed");
};
[[NSOperationQueue mainQueue] addOperation:operation];
-~~~
+```
For example, you could set a completion block on a network operation to do something with the response data from the server once it's finished loading.
diff --git a/2014-07-21-xctestcase.md b/2014-07-21-xctestcase.md
index 3e33d6e7..23905196 100644
--- a/2014-07-21-xctestcase.md
+++ b/2014-07-21-xctestcase.md
@@ -1,21 +1,20 @@
---
title: "XCTestCase / XCTestExpectation / measureBlock()"
-author: Mattt Thompson
+author: Mattt
category: Xcode
excerpt: "This week, we'll take a look at `XCTest`, the testing framework built into Xcode, as well as the exciting new additions in Xcode 6: `XCTestExpectation` and performance tests."
revisions:
- "2015-04-07": Added note about location of call to `fulfill()`; new Objective-C examples
-hiddenlang: ""
+ "2015-04-07": Added note about location of call to `fulfill()`; new Objective-C examples
status:
- swift: 1.2
- reviewed: June 25, 2015
+ swift: 1.2
+ reviewed: June 25, 2015
---
Although iOS 8 and Swift has garnered the lion's share of attention of the WWDC 2014 announcements, the additions and improvements to testing in Xcode 6 may end up making some of the most profound impact in the long-term.
This week, we'll take a look at `XCTest`, the testing framework built into Xcode, as well as the exciting new additions in Xcode 6: `XCTestExpectation` and performance tests.
-* * *
+---
Most Xcode project templates now support testing out-of-the-box. For example, when a new iOS app is created in Xcode with `⇧⌘N`, the resulting project file will be configured with two top-level groups (in addition to the "Products" group): "AppName" & "AppNameTests". The project's auto-generated scheme enables the shortcut `⌘R` to build and run the executable target, and `⌘U` to build and run the test target.
@@ -44,7 +43,8 @@ class Tests: XCTestCase {
}
}
```
-```objective-c
+
+```objc
@interface Tests : XCTestCase
@property NSCalendar *calendar;
@@ -80,10 +80,11 @@ override func setUp() {
locale = NSLocale(localeIdentifier: "en_US")
}
```
-```objective-c
+
+```objc
- (void)setUp {
[super setUp];
-
+
self.calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
self.locale = [NSLocale localeWithLocaleIdentifier:@"en_US"];
}
@@ -107,7 +108,8 @@ func testOnePlusOneEqualsTwo() {
XCTAssertEqual(1 + 1, 2, "one plus one should equal two")
}
```
-```objective-c
+
+```objc
- (void)testOnePlusOneEqualsTwo {
XCTAssertEqual(1 + 1, 2, "one plus one should equal two");
}
@@ -124,7 +126,8 @@ To be entirely reductionist, all of the `XCTest` assertions come down to a singl
```swift
XCTAssert(expression, format...)
```
-```objective-c
+
+```objc
XCTAssert(expression, format...);
```
@@ -140,7 +143,8 @@ For `Bool` values, or simple boolean expressions, use `XCTAssertTrue` & `XCTAsse
XCTAssertTrue(expression, format...)
XCTAssertFalse(expression, format...)
```
-```objective-c
+
+```objc
XCTAssertTrue(expression, format...);
XCTAssertFalse(expression, format...);
```
@@ -155,7 +159,8 @@ When testing whether two values are equal, use `XCTAssert[Not]Equal` for scalar
XCTAssertEqual(expression1, expression2, format...)
XCTAssertNotEqual(expression1, expression2, format...)
```
-```objective-c
+
+```objc
XCTAssertEqual(expression1, expression2, format...);
XCTAssertNotEqual(expression1, expression2, format...);
@@ -165,13 +170,14 @@ XCTAssertNotEqualObjects(expression1, expression2, format...);
> `XCTAssert[Not]EqualObjects` is not necessary in Swift, since there is no distinction between scalars and objects.
-When specifically testing whether two `Double`, `Float`, or other floating-point values are equal, use `XCTAssert[Not]EqualWithAccuracy`, to account for any issues with [floating point accuracy](http://en.wikipedia.org/wiki/Floating_point#Representable_numbers.2C_conversion_and_rounding):
+When specifically testing whether two `Double`, `Float`, or other floating-point values are equal, use `XCTAssert[Not]EqualWithAccuracy`, to account for any issues with [floating point accuracy](https://en.wikipedia.org/wiki/Floating_point#Representable_numbers.2C_conversion_and_rounding):
```swift
XCTAssertEqualWithAccuracy(expression1, expression2, accuracy, format...)
XCTAssertNotEqualWithAccuracy(expression1, expression2, accuracy, format...)
```
-```objective-c
+
+```objc
XCTAssertEqualWithAccuracy(expression1, expression2, accuracy, format...);
XCTAssertNotEqualWithAccuracy(expression1, expression2, accuracy, format...);
```
@@ -186,7 +192,8 @@ Use `XCTAssert[Not]Nil` to assert the existence (or non-existence) of a given va
XCTAssertNil(expression, format...)
XCTAssertNotNil(expression, format...)
```
-```objective-c
+
+```objc
XCTAssertNil(expression, format...);
XCTAssertNotNil(expression, format...);
```
@@ -198,7 +205,8 @@ Finally, the `XCTFail` assertion will always fail:
```swift
XCTFail(format...)
```
-```objective-c
+
+```objc
XCTFail(format...);
```
@@ -206,7 +214,7 @@ XCTFail(format...);
### Performance Testing
-New in Xcode 6 is the ability to [benchmark the performance of code](http://nshipster.com/benchmarking/):
+New in Xcode 6 is the ability to [benchmark the performance of code](https://nshipster.com/benchmarking/):
```swift
func testDateFormatterPerformance() {
@@ -221,7 +229,8 @@ func testDateFormatterPerformance() {
}
}
```
-```objective-c
+
+```objc
- (void)testDateFormatterPerformance {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateStyle = NSDateFormatterLongStyle;
@@ -235,7 +244,7 @@ func testDateFormatterPerformance() {
}
```
-The measured block is executed ten times and the test output shows the average execution time as well as individual run times and standard deviation:
+The test output shows the average execution time for the measured block as well as individual run times and standard deviation:
```
Test Case '-[_Tests testDateFormatterPerformance]' started.
@@ -243,7 +252,7 @@ Test Case '-[_Tests testDateFormatterPerformance]' started.
Test Case '-[_Tests testDateFormatterPerformance]' passed (0.274 seconds).
```
-Performance tests help establish a per-device baseline of performance for hot code paths and will fail if execution time becomes significantly slower. Sprinkle them into your test cases to ensure that significant algorithms and procedures remain performant as time goes on.
+Performance tests help establish a baseline of performance for hot code paths. Sprinkle them into your test cases to ensure that significant algorithms and procedures remain performant as time goes on.
## XCTestExpectation
@@ -254,7 +263,8 @@ To make a test asynchronous, first create an expectation with `expectationWithDe
```swift
let expectation = expectationWithDescription("...")
```
-```objective-c
+
+```objc
XCTestExpectation *expectation = [self expectationWithDescription:@"..."];
```
@@ -262,12 +272,13 @@ Then, at the bottom of the method, add the `waitForExpectationsWithTimeout` meth
```swift
waitForExpectationsWithTimeout(10) { error in
- // ...
+ <#...#>
}
```
-```objective-c
+
+```objc
[self waitForExpectationsWithTimeout:10 handler:^(NSError *error) {
- // ...
+ <#...#>
}];
```
@@ -276,7 +287,8 @@ Now, the only remaining step is to `fulfill` that `expecation` in the relevant c
```swift
expectation.fulfill()
```
-```objective-c
+
+```objc
[expectation fulfill];
```
@@ -286,14 +298,14 @@ Here's an example of how the response of an asynchronous networking request can
```swift
func testAsynchronousURLConnection() {
- let URL = NSURL(string: "http://nshipster.com/")!
+ let URL = NSURL(string: "https://nshipster.com/")!
let expectation = expectationWithDescription("GET \(URL)")
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(URL) { data, response, error in
XCTAssertNotNil(data, "data should not be nil")
XCTAssertNil(error, "error should be nil")
-
+
if let HTTPResponse = response as? NSHTTPURLResponse,
responseURL = HTTPResponse.URL,
MIMEType = HTTPResponse.MIMEType
@@ -318,19 +330,20 @@ func testAsynchronousURLConnection() {
}
}
```
-```objective-c
+
+```objc
- (void)testAsynchronousURLConnection {
- NSURL *URL = [NSURL URLWithString:@"http://nshipster.com/"];
+ NSURL *URL = [NSURL URLWithString:@"https://nshipster.com/"];
NSString *description = [NSString stringWithFormat:@"GET %@", URL];
XCTestExpectation *expectation = [self expectationWithDescription:description];
-
+
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithURL:URL
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
XCTAssertNotNil(data, "data should not be nil");
XCTAssertNil(error, "error should be nil");
-
+
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
XCTAssertEqual(httpResponse.statusCode, 200, @"HTTP response status code should be 200");
@@ -339,15 +352,15 @@ func testAsynchronousURLConnection() {
} else {
XCTFail(@"Response was not NSHTTPURLResponse");
}
-
+
[expectation fulfill];
}];
-
+
[task resume];
-
+
[self waitForExpectationsWithTimeout:task.originalRequest.timeoutInterval handler:^(NSError *error) {
if (error != nil) {
- NSLog(@"Error: %@", error.localizedDescription);
+ NSLog(@"Error: %@", error.localizedDescription);
}
[task cancel];
}];
@@ -356,11 +369,11 @@ func testAsynchronousURLConnection() {
## Mocking in Swift
-With first-class support for asynchronous testing, Xcode 6 seems to have fulfilled all of the needs of a modern test-driven developer. Well, perhaps save for one: [mocking](http://en.wikipedia.org/wiki/Mock_object).
+With first-class support for asynchronous testing, Xcode 6 seems to have fulfilled all of the needs of a modern test-driven developer. Well, perhaps save for one: [mocking](https://en.wikipedia.org/wiki/Mock_object).
Mocking can be a useful technique for isolating and controlling behavior in systems that, for reasons of complexity, non-determinism, or performance constraints, do not usually lend themselves to testing. Examples include simulating specific networking interactions, intensive database queries, or inducing states that might emerge under a particular race condition.
-There are a couple of [open source libraries](http://nshipster.com/unit-testing/#open-source-libraries) for creating mock objects and [stubbing](http://en.wikipedia.org/wiki/Test_stub) method calls, but these libraries largely rely on Objective-C runtime manipulation, something that is not currently possible with Swift.
+There are a couple of [open source libraries](https://nshipster.com/unit-testing/#open-source-libraries) for creating mock objects and [stubbing](https://en.wikipedia.org/wiki/Test_stub) method calls, but these libraries largely rely on Objective-C runtime manipulation, something that is not currently possible with Swift.
However, this may not actually be necessary in Swift, due to its less-constrained syntax.
@@ -391,7 +404,7 @@ func testFetchRequestWithMockedManagedObjectContext() {
}
```
-* * *
+---
With Xcode 6, we've finally arrived: **the built-in testing tools are now good enough to use on their own**. That is to say, there are no particularly compelling reasons to use any additional abstractions in order to provide acceptable test coverage for the vast majority apps and libraries. Except in extreme cases that require extensive stubbing, mocking, or other exotic test constructs, XCTest assertions, expectations, and performance measurements should be sufficient.
diff --git a/2014-08-04-alamofire.md b/2014-08-04-alamofire.md
index 935c441c..9df08dda 100644
--- a/2014-08-04-alamofire.md
+++ b/2014-08-04-alamofire.md
@@ -1,15 +1,16 @@
---
title: Alamofire
-author: Mattt Thompson
+author: Mattt
category: Open Source
excerpt: "Although we still have a few months to wait before we can ship apps in Swift, there is already a proliferation of open source projects built with this new language. One such project is Alamofire."
+retired: true
status:
- swift: 1.1
+ swift: 1.1
---
Swift has hit a reset button on the iOS developer community. It's really been something to behold for seasoned Objective-C developers.
-Literally overnight, conversations shifted from [namespacing](http://nshipster.com/namespacing/) and [swizzling](http://nshipster.com/method-swizzling/) to [generics](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Generics.html#//apple_ref/doc/uid/TP40014097-CH26-XID_272) and [type inference](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/TypeCasting.html#//apple_ref/doc/uid/TP40014097-CH22-XID_487). At a time of the year when Twitter would normally be abuzz with discussion about the latest APIs or speculation about upcoming hardware announcements, the discourse has been dominated by all things Swift.
+Literally overnight, conversations shifted from [namespacing](https://nshipster.com/namespacing/) and [swizzling](https://nshipster.com/method-swizzling/) to [generics](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Generics.html#//apple_ref/doc/uid/TP40014097-CH26-XID_272) and [type inference](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/TypeCasting.html#//apple_ref/doc/uid/TP40014097-CH22-XID_487). At a time of the year when Twitter would normally be abuzz with discussion about the latest APIs or speculation about upcoming hardware announcements, the discourse has been dominated by all things Swift.
A new language with new syntax and evolving conventions, Swift has captured the attention and imagination of iOS & OS X developers both old and new.
@@ -19,7 +20,7 @@ One such project is [Alamofire](https://github.com/Alamofire/Alamofire). This we
> Full Disclosure: this article, as with the rest of NSHipster, is written by the creator of AFNetworking and Alamofire. While this makes me qualified to write about the technical details and direction of these projects, it certainly doesn't allow for an objective take on their relative merits. So take all of this with a grain of salt.
-* * *
+---
[Alamofire](https://github.com/Alamofire/Alamofire) is an HTTP networking library written in Swift. It leverages NSURLSession and the Foundation URL Loading System to provide first-class networking capabilities in a convenient Swift interface.
@@ -58,7 +59,7 @@ Alamofire.request(.GET, "http://httpbin.org/get")
Even minor syntactic differences can have wide-reaching implications for language conventions.
-Chief among the complaints lodged against Objective-C was its use of square brackets for denoting [message passing](http://en.wikipedia.org/wiki/Message_passing). One of the practical implications of `[ ]` syntax is the difficulty in chaining methods together. Even with Xcode autocomplete, `@property` dot syntax, and [key-value coding key-paths](http://nshipster.com/kvc-collection-operators/), it is still rare to see deeply nested invocations.
+Chief among the complaints lodged against Objective-C was its use of square brackets for denoting [message passing](https://en.wikipedia.org/wiki/Message_passing). One of the practical implications of `[ ]` syntax is the difficulty in chaining methods together. Even with Xcode autocomplete, `@property` dot syntax, and [key-value coding key-paths](https://nshipster.com/kvc-collection-operators/), it is still rare to see deeply nested invocations.
> In many ways, the concession of introducing dot syntax for properties in Objective-C 2.0 only served to escalate tensions further, although conventions have started to solidify in recent years.
@@ -88,7 +89,7 @@ Closures are deeply integrated into Swift, so much so that if a methods's last a
The chained methods in the previous example show this in action. For typical usage, it's really convenient to omit the syntactic cruft.
-### Optional Arguments & Flexible Method Signatures
+### Optional Arguments & Flexible Method Signatures
When communicating with web APIs, it's common to send parameters with URL queries or HTTP body data:
@@ -125,7 +126,6 @@ The `encode` method on each `ParameterEncoding` case transforms a request and se
Given a complex, nested set of parameters, encoding and sending as JSON is recommended:
-
> There are no standards defining the encoding of data structures into URL-encoded query parameters, meaning that parsing behavior can vary between web application implementations. Even worse, there are certain structures that cannot be unambiguously represented by a query string. This is why JSON (or XML or plist) encoding is recommended for anything more complex than key-value, if the web API supports it.
```swift
@@ -188,12 +188,12 @@ lazy var defaultHeaders: [String: String] = {
// Accept-Language HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
let acceptLanguage: String = {
- // ...
+ <#...#>
}()
// User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43
let userAgent: String = {
- // ...
+ <#...#>
}()
return ["Accept-Encoding": acceptEncoding,
@@ -219,12 +219,12 @@ override var data: NSData! {
There's a lot baked into the < 1000 LOC comprising Alamofire. Any aspiring Swift developer or API author would be advised to [peruse through the source code](https://github.com/Alamofire/Alamofire) to get a better sense of what's going on.
-* * *
+---
> For anyone wondering where this leaves AFNetworking, don't worry: **AFNetworking is stable and reliable, and isn't going anywhere.** In fact, over the coming months, a great deal of work is going to be put into improving test coverage and documentation for AFNetworking 2 and its first-party extensions.
> It's also important to note that AFNetworking can be easily used from Swift code, just like any other Objective-C code. Alamofire is a separate project investigating the implications of new language features and conventions on the problem of making HTTP requests.
-Alamofire 1.0 is scheduled to coincide with the 1.0 release of Swift... whenever that is. Part of that milestone is [complete documentation](http://nshipster.com/swift-documentation/), and 100% Unit Test Coverage, making use of the new [Xcode 6 testing infrastructure](http://nshipster.com/xctestcase/) & [httpbin](http://httpbin.org) by [Kenneth Reitz](http://www.kennethreitz.org).
+Alamofire 1.0 is scheduled to coincide with the 1.0 release of Swift... whenever that is. Part of that milestone is [complete documentation](https://nshipster.com/swift-documentation/), and 100% Unit Test Coverage, making use of the new [Xcode 6 testing infrastructure](https://nshipster.com/xctestcase/) & [httpbin](http://httpbin.org) by [Kenneth Reitz](http://www.kennethreitz.org).
We're all doing our best to understand how to design, implement, and distribute code in Swift. Alamofire is just one of many exciting new libraries that will guide the development of the language and community in the coming months and years. For anyone interested in being part of this, I welcome your contributions. As the mantra goes: [pull requests welcome](https://github.com/Alamofire/Alamofire/compare/).
diff --git a/2014-08-11-swift-operators.md b/2014-08-11-swift-operators.md
index a38f9a10..01e11d9e 100644
--- a/2014-08-11-swift-operators.md
+++ b/2014-08-11-swift-operators.md
@@ -1,429 +1,559 @@
---
title: Swift Operators
-author: Mattt Thompson
+author: Mattt
category: Swift
tags: swift
-excerpt: "Operators in Swift are among the most interesting and indeed controversial features of this new language."
+excerpt: >-
+ Operators are what do the work of a program.
+ They are the very execution of an executable;
+ the teleological driver of every process.
+revisions:
+ "2014-08-11": Original publication
+ "2018-10-03": Updated for Swift 4.2
status:
- swift: 2.0
- reviewed: September 8, 2015
+ swift: 4.2
+ reviewed: October 3, 2018
---
-What would a program be without statements? A mish-mash of classes, namespaces, conditionals, loops, and namespaces signifying nothing.
+What would a program be without operators?
+A mishmash of classes, namespaces, conditionals, loops, and namespaces
+signifying nothing.
-Statements are what do the work of a program. They are the very execution of an executable.
+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.
-If we were to take apart a statement—say `1 + 2`—decomposing it into its constituent parts, we would find an operator and operands:
+## Operator Precedence and Associativity
-| 1 | + | 2 |
-|:-------------------:|:-------------------:|:-------------------:|
-| left operand | operator | right operand |
+If we were to dissect an expression ---
+say `1 + 2` ---
+and decompose it into constituent parts,
+we would find one operator and two operands:
-Although expressions are flat, the compiler will construct a tree representation, or AST:
+| 1 | + | 2 |
+| :----------: | :------: | :-----------: |
+| left operand | operator | right operand |
-
+Expressions are expressed in a single, flat line of code,
+from which the compiler constructs an
+AST,
+or abstract syntax tree:
+{% asset swift-operators-one-plus-two.svg alt="1 + 2 AST" %}
-Compound statements, like `1 + 2 + 3`
+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.
-| (1 + 2) | + | 3 |
-|:-------------------:|:-------------------:|:-------------------:|
-| left operand | operator | right operand |
+Operator precedence rules,
+[similar to the ones you learned in primary school](https://en.wikipedia.org/wiki/Order_of_operations),
+determine the order in which different kinds of operators are evaluated.
+In this case, multiplication has a higher precedence than addition,
+so `2 * 3` evaluates first.
+| 1 | + | (2 \* 3) |
+| :----------: | :------: | :-----------: |
+| left operand | operator | right operand |
-
+{% asset swift-operators-one-plus-two-times-three.svg alt="1 + 2 * 3 AST" %}
-Or, to take an even more complex statement, `1 + 2 * 3 % 4`, the compiler would use operator precedence to resolve the expression into a single statement:
+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`.
-| 1 | + | ((2 * 3) % 4) |
-|:-------------------:|:-------------------:|:-------------------------:|
-| left operand | operator | right operand |
+| (5 - 2) | + | 3 |
+| :----------: | :------: | :-----------: |
+| left operand | operator | right operand |
-
-
-
-Operator precedence rules, similar to the ones [you learned in primary school](http://en.wikipedia.org/wiki/Order_of_operations), provide a canonical ordering for any compound statement:
-
-```
-1 + 2 * 3 % 4
-1 + ((2 * 3) % 4)
-1 + (6 % 4)
-1 + 2
-```
-
-However, consider the statement `5 - 2 + 3`. Addition and subtraction have the same operator precedence, but evaluating the subtraction first `(5 - 2) + 3` yields 6, whereas evaluating subtraction after addition, `5 - (2 + 3)`, yields `0`. In code, arithmetic operators are left-associative, meaning that the left hand side will evaluate first (`(5 - 2) + 3`).
-
-Operators can be unary and ternary as well. The `!` prefix operator negates a logical value of the operand. The `?:` ternary operator collapses an `if-else` expression, by evaluating the statement to the left of the `?` in order to either execute the statement left of the `:` (statement is `true`) or right of `:` (statement is `false`).
+{% asset swift-operators-one-plus-two-plus-three.svg alt="5 - 2 + 3 AST" %}
## Swift Operators
-Swift includes a set of operators that should be familiar to C or Objective-C developers, with a few additions (notably, the range and nil coalescing operators):
-
-### Prefix
+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](https://en.wikipedia.org/wiki/Reverse_Polish_notation))_.
+The Infix operators are grouped below
+according to their associativity
+and precedence level, in descending order:
+
+
+
+#### BitwiseShiftPrecedence
+
+{::nomarkdown}
+
+{:/}
+
+
+
+{% info %}
+
+Operator precedence groups were originally defined with numerical precedence.
+For example, multiplicative operators defined a precedence value of 150,
+so they were evaluated before additive operators,
+which defined a precedence value of 140.
+
+In Swift 3,
+operators changed to define precedence by partial ordering
+to form a DAG
+or [directed acyclic graph](https://en.wikipedia.org/wiki/Directed_acyclic_graph).
+For detailed information about this change,
+read Swift Evolution proposal
+[SE-0077 "Improved operator declarations"](https://github.com/apple/swift-evolution/blob/master/proposals/0077-operator-precedence.md).
+
+{% endinfo %}
+
+### Unary Operators
+
+In addition to binary operators that take two operands,
+there are also unary operators,
+which take a single operand.
+
+#### Prefix Operators
+
+Prefix operators come before the expression they operate on.
+Swift defines a handful of these by default:
- `+`: Unary plus
- `-`: Unary minus
- `!`: Logical NOT
- `~`: Bitwise NOT
+- `...`: Open-ended partial range
+- `..<`: Closed partial range
-### Infix
-
-
-
-
Exponentiative {precedence 160}
-
-
-
<<
Bitwise left shift
-
>>
Bitwise right shift
-
-
-
-
-
Multiplicative { associativity left precedence 150 }
-
-
-
*
Multiply
-
/
Divide
-
%
Remainder
-
&*
Multiply, ignoring overflow
-
&/
Divide, ignoring overflow
-
&%
Remainder, ignoring overflow
-
&
Bitwise AND
-
-
-
-
Additive { associativity left precedence 140 }
-
-
-
+
Add
-
-
Subtract
-
&+
Add with overflow
-
&-
Subtract with overflow
-
|
Bitwise OR
-
^
Bitwise XOR
-
-
-
-
Range { precedence 135 }
-
-
-
..<
Half-open range
-
...
Closed range
-
-
-
-
Cast { precedence 132 }
-
-
-
is
Type check
-
as
Type cast
-
-
-
-
Comparative { precedence 130 }
-
-
-
<
Less than
-
<=
Less than or equal
-
>
Greater than
-
>=
Greater than or equal
-
==
Equal
-
!=
Not equal
-
===
Identical
-
!==
Not identical
-
~=
Pattern match
-
-
-
-
Conjunctive { associativity left precedence 120 }
-
-
-
&&
Logical AND
-
-
-
-
Disjunctive { associativity left precedence 110 }
-
-
-
||
Logical OR
-
-
-
-
Nil Coalescing { associativity right precedence 110 }
-
-
-
??
Nil coalescing
-
-
-
-
Ternary Conditional { associativity right precedence 100 }
-
-
-
?:
Ternary conditional
-
-
-
-
Assignment { associativity right precedence 90 }
-
-
-
=
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
-
-
-
-### Member Functions
-
-In addition to the aforementioned standard operators, there are some _de facto_ operators defined by the language:
-
-- `.`: Member Access
-- `?`: Optional
-- `!`: Forced-Value
-- `[]`: Subscript
-- `[]=`: Subscript Assignment
-
-## Overloading
-
-Swift has the ability to overload operators such that existing operators, like `+`, can be made to work with additional types.
-
-To overload an operator, simply define a new function for the operator symbol, taking the appropriate number and type of arguments.
-
-For example, to overload `*` to repeat a string a specified number of times:
+For example,
+the `!` prefix operator
+negates a logical value of its operand
+and the `-` prefix operator
+negates the numeric value of its operand.
```swift
-func * (left: String, right: Int) -> String {
- if right <= 0 {
- return ""
- }
-
- var result = left
- for _ in 1.. [Double] {
- var sum = [Double](count: left.count, repeatedValue: 0.0)
- for (i, _) in enumerate(left) {
- sum[i] = left[i] + right[i]
- }
-
- return sum
-}
+true ? "Yes" : "No" // "Yes"
```
-The result is now an array with the pairwise sums of each element, expressed as `Double`:
+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).
-```swift
-[1, 2] + [3, 4] // [4.0, 6.0]
-```
+## Operator Overloading
-And if the operator were also overloaded to work with `Int` types, with:
+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.
-```swift
-func +(left: [Int], right: [Int]) -> [Int] {
- var sum = [Int](count: left.count, repeatedValue: 0)
- for (i, _) in enumerate(left) {
- sum[i] = left[i] + right[i]
- }
+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]` ).
- return sum
-}
-```
+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.
-The result would then be an array of pairwise sums, expressed as `Int`.
+For example, to overload the `*` operator
+to repeat a `String` a specified number of times,
+you'd declare the following top-level function:
```swift
-[1, 2] + [3, 4] // [4, 6]
-```
+func * (lhs: String, rhs: Int) -> String {
+ guard rhs > 0 else {
+ return ""
+ }
-Herein lies the original sin of operator overloading: **ambiguous semantics**.
+ return String(repeating: lhs, count: rhs)
+}
-Having been limited to basic arithmetic operators across many years and programming languages, overloading of operators has become commonplace:
+"hello" * 3 // hellohellohello
+```
-- Computing Sum of Integers: `1 + 2 // 3`
-- Computing Sum of Floats: `1.0 + 2.0 // 3.0`
-- Appending to String: `"a" + "b" // "ab"`
-- Appending to Array: `["foo"] + ["bar"] // ["foo", "bar"]`
+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)
-It makes sense that `+` would work on numbers—that's just math. But think about it: _why should adding two strings together concatenate them_? `1 + 2` isn't `12` ([except in Javascript](https://www.destroyallsoftware.com/talks/wat)). Is this really intuitive, or is it just familiar.
+Consider the following statement:
-> PHP uses `.` for string concatenation (which is objectively a terrible idea). Objective-C allows consecutive string literals to be appended with whitespace.
+```swift
+[1, 2] + [3, 4] // [1, 2, 3, 4]
+```
-In the run-up to its initial stable release, Swift still has some work to do in resolving ambiguities in operator semantics. Recent changes, such as the addition of the `nil` coalescing operator (`??`), and the decision for optionals not to conform to `BooleanType` (confusing in the case of `Bool?`) are encouraging, and demonstrate the need for us to collectively ask ourselves _"does this really make sense?"_, and file radars appropriately.
+By default, the `+` operator concatenates the elements of both arrays,
+and is implemented using a generic function definition.
-> I'm specifically concerned about the semantics of array operators, as demonstrated in the previous example. My 2 cents: arrays should forego the `+` and `-` operators in lieu of `<<`:
+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:
```swift
-func << (inout left: [T], right: [T]) -> [T] {
- left.extend(right)
- return left
+// 👿
+func + (lhs: [Double], rhs: [Double]) -> [Double] {
+ return zip(lhs, rhs).map(+)
}
-func << (inout left: [T], right: T) -> [T] {
- left.append(right)
- return left
-}
+[1.0, 3.0, 5.0] + [2.0, 4.0, 6.0] // [3.0, 7.0, 11.0]
```
-## Custom Operators
+Herein lies the original sin of operator overloading:
+**ambiguous semantics**.
-An even more controversial and exciting feature is the ability to define custom operators.
+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](https://www.destroyallsoftware.com/talks/wat)).
+Is this really intuitive? ...or is it just _familiar_.
-Consider the arithmetic operator found in many programming languages, but missing in Swift is `**`, which raises the left hand number to the power of the right hand number (the `^` symbol, commonly used for superscripts, is already used to perform a [bitwise XOR](http://en.wikipedia.org/wiki/Bitwise_operation#XOR)).
+Something to keep in mind when deciding whether to overload an existing operator.
-To add this operator in Swift, first declare the operator:
+{% info %}
-```swift
-infix operator ** { associativity left precedence 160 }
-```
+By comparison,
+PHP uses `.` for string concatenation,
+whereas SQL uses `||`;
+Objective-C doesn't have an operator, per se,
+but will append consecutive string literals with whitespace.
-- `infix` specifies that it is a binary operator, taking a left and right hand argument
-- `operator` is a reserved word that must be preceded with either `prefix`, `infix`, or `postfix`
-- `**` is the operator itself
-- `associativity left` means that operations are grouped from the left
-- `precedence 160` means that it will evaluate with the same precedence as the exponential operators `<<` and `>>` (left and right bitshift).
+{% endinfo %}
-```swift
-func ** (left: Double, right: Double) -> Double {
- return pow(left, right)
-}
+## Defining Custom Operators
-2 ** 3
-// 8
-```
+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](https://en.wikipedia.org/wiki/Bitwise_operation#XOR) operator).
-When creating custom operators, make sure to also create the corresponding assignment operator, if appropriate:
+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:
```swift
-infix operator **= { associativity right precedence 90 }
-func **= (inout left: Double, right: Double) {
- left = left ** right
+precedencegroup ExponentiationPrecedence {
+ associativity: right
+ higherThan: MultiplicationPrecedence
}
```
-> Note that `left` is `inout`, which makes sense, since assignment mutates the original value.
+Now we can declare the operator itself:
-### Custom Operators with Protocol and Method
-
-Function definitions for the operators themselves should be extremely simple—a single LOC, really. For anything more complex, some additional setup is warranted.
+```swift
+infix operator ** : ExponentiationPrecedence
+```
-Take, for example, a custom operator, `=~`, which returns whether the left hand side matches a regular expression on the right hand side:
+Finally,
+we implement a top-level function using our new operator:
```swift
-protocol RegularExpressionMatchable {
- func match(pattern: String, options: NSRegularExpressionOptions) throws -> Bool
-}
+import Darwin
-extension String: RegularExpressionMatchable {
- func match(pattern: String, options: NSRegularExpressionOptions = []) throws -> Bool {
- let regex = try NSRegularExpression(pattern: pattern, options: options)
- return regex.numberOfMatchesInString(self, options: [], range: NSRange(location: 0, length: 0.distanceTo(utf16.count))) != 0
- }
+func ** (lhs: Double, rhs: Double) -> Double {
+ return pow(lhs, rhs)
}
-infix operator =~ { associativity left precedence 130 }
-func =~ (left: T, right: String) -> Bool {
- return try! left.match(right, options: [])
-}
+2 ** 3 // 8
```
-- First, a `RegularExpressionMatchable` `protocol` is declared, with a single method for matching regular expressions.
-- Next, an `extension` adding conformance to this `protocol` to `String` is declared, with a provided implementation of `match`, using `NSRegularExpression`.
-- Finally, the `=~` operator is declared and implemented on a generic type conforming to `RegularExpressionMatchable`.
+{% info %}
+
+We need to import the Darwin module
+to access the standard math function, `pow(_:_:)`.
+(Alternatively, we could import Foundation instead to the same effect.)
-By doing this, a user has the option to use the `match` function instead of the operator. It also has the added benefit of greater flexibility in what options are passed into the method.
+{% endinfo %}
+
+When you create a custom operator,
+consider providing a mutating variant as well:
```swift
-let cocoaClassPattern = "^[A-Z]{2,}[A-Za-z0-9]+$"
+infix operator **= : AssignmentPrecedence
+func **= (lhs: inout Double, rhs: Double) {
+ lhs = pow(lhs, rhs)
+}
-try? "NSHipster".match(cocoaClassPattern) // true
-"NSHipster" =~ cocoaClassPattern // true
+var n: Double = 10
+n **= 1 + 2 // n = 1000
```
-This is all to say: **a custom operator should only ever be provided as a convenience for an existing function.**
-
### Use of Mathematical Symbols
-Custom operators can contain any of the following ASCII characters /, =, -, +, !, *, %, <, >, &, |, ^, or ~, or any of the Unicode characters in the "Math Symbols" character set.
+A custom operator can use combinations of the characters
+`/`, `=`, `-`, `+`, `!`, `*`, `%`, `<`, `>`, `&`, `|`, `^`, or `~`,
+and any characters found in the
+[Mathematical Operators](https://en.wikipedia.org/wiki/Mathematical_Operators)
+Unicode block, among others.
-This makes it possible to take the square root of a number with a single `√` prefix operator (`⌥v`):
+This makes it possible to take the square root of a number
+with a single `√` prefix operator:
```swift
-prefix operator √ {}
-prefix func √ (number: Double) -> Double {
- return sqrt(number)
+import Darwin
+
+prefix operator √
+prefix func √ (_ value: Double) -> Double {
+ return sqrt(value)
}
-√4
-// 2
+√4 // 2
```
-Or consider the `±` operator, which can be used either as an `infix` or `prefix` to return a tuple with the sum and difference:
+Or consider the `±` operator,
+which can be used either as an infix or prefix operator
+to return a tuple with the sum and difference:
```swift
-infix operator ± { associativity left precedence 140 }
-func ± (left: Double, right: Double) -> (Double, Double) {
- return (left + right, left - right)
+infix operator ± : AdditionPrecedence
+func ± (lhs: T, rhs: T) -> (T, T) {
+ return (lhs + rhs, lhs - rhs)
}
-prefix operator ± {}
-prefix func ± (value: Double) -> (Double, Double) {
+prefix operator ±
+prefix func ± (_ value: T) -> (T, T) {
return 0 ± value
}
-2 ± 3
-// (5, -1)
+2 ± 3 // (5, -1)
-±4
-// (4, -4)
+±4 // (4, -4)
```
-> For more examples of functions using mathematical notation in Swift, check out [Euler](https://github.com/mattt/Euler).
-
-Custom operators are hard to type, and therefore hard to use. **Exercise restraint when using custom operators with exotic symbols**. After all, code should not be copy-pasted.
-
-* * *
+{% info %}
-Operators in Swift are among the most interesting and indeed controversial features of this new language.
+For more examples of functions using mathematical notation in Swift,
+check out [Euler](https://github.com/mattt/Euler).
-When overriding or defining new operators in your own code, make sure to follow these guidelines:
+{% endinfo %}
-## Guidelines for Swift Operators
+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.
-1. Don't create an operator unless its meaning is obvious and undisputed. Seek out any potential conflicts to ensure semantic consistency.
-2. Custom operators should only be provided as a convenience. Complex functionality should always be implemented in a function, preferably one specified as a generic using a custom protocol.
-3. Pay attention to the precedence and associativity of custom operators. Find the closest existing class of operators and use the appropriate precedence value.
-4. If it makes sense, be sure to implement assignment shorthand for a custom operator (e.g. `+=` for `+`).
+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 `+`).
diff --git a/2014-08-18-swift-literal-convertible.md b/2014-08-18-swift-literal-convertible.md
deleted file mode 100644
index 2c97e1a0..00000000
--- a/2014-08-18-swift-literal-convertible.md
+++ /dev/null
@@ -1,272 +0,0 @@
----
-title: Swift Literal Convertibles
-author: Mattt Thompson
-category: Swift
-tags: swift
-excerpt: "Last week, we wrote about overloading and creating custom operators in Swift, a language feature that is as powerful as it is controversial. By all accounts, this week's issue threatens to be equally polarizing, as it covers a feature of Swift that is pervasive, yet invisible: literal convertibles."
-status:
- swift: 1.2
----
-
-Last week, we wrote about [overloading and creating custom operators](http://nshipster.com/swift-operators/) in Swift, a language feature that is as powerful as it is controversial.
-
-By all accounts, this week's issue threatens to be equally polarizing, as it covers a feature of Swift that is pervasive, yet invisible: literal convertibles.
-
-* * *
-
-In code, a _literal_ is notation representing a fixed value. Most languages define literals for logical values, numbers, strings, and often arrays and dictionaries.
-
-```swift
-let int = 57
-let float = 6.02
-let string = "Hello"
-```
-
-Literals are so ingrained in a developer's mental model of programming that most of us don't actively consider what the compiler is actually doing (thereby remaining blissfully unaware of neat tricks like [string interning](http://en.wikipedia.org/wiki/String_interning)).
-
-Having a shorthand for these essential building blocks makes code easier to both read and write.
-
-In Swift, developers are provided a hook into how values are constructed from literals, called _literal convertible protocols_.
-
-The standard library defines 10 such protocols:
-
-- `ArrayLiteralConvertible`
-- `BooleanLiteralConvertible`
-- `DictionaryLiteralConvertible`
-- `ExtendedGraphemeClusterLiteralConvertible`
-- `FloatLiteralConvertible`
-- `NilLiteralConvertible`
-- `IntegerLiteralConvertible`
-- `StringLiteralConvertible`
-- `StringInterpolationConvertible`
-- `UnicodeScalarLiteralConvertible`
-
-Any `class` or `struct` conforming to one of these protocols will be eligible to have an instance of itself statically initialized from the corresponding literal.
-
-It's what allows literal values to "just work" across the language.
-
-Take optionals, for example.
-
-## NilLiteralConvertible and Optionals
-
-One of the best parts of optionals in Swift is that the underlying mechanism is actually defined in the language itself:
-
-```swift
-enum Optional : Reflectable, NilLiteralConvertible {
- case None
- case Some(T)
- init()
- init(_ some: T)
- init(nilLiteral: ())
-
- func map(f: (T) -> U) -> U?
- func getMirror() -> MirrorType
-}
-```
-
-Notice that `Optional` conforms to the `NilLiteralConvertible` protocol:
-
-```swift
-protocol NilLiteralConvertible {
- init(nilLiteral: ())
-}
-```
-
-Now consider the two statements:
-
-```swift
-var a: AnyObject = nil // !
-var b: AnyObject? = nil
-```
-
-The declaration of `var a` generates the compiler warning `Type 'AnyObject' does not conform to the protocol 'NilLiteralConvertible`, while the declaration `var b` works as expected.
-
-Under the hood, when a literal value is assigned, the Swift compiler consults the corresponding `protocol` (in this case `NilLiteralConvertible`), and calls the associated initializer (`init(nilLiteral: ())`).
-
-Although the implementation of `init(nilLiteral: ())` is private, the end result is that an `Optional` set to `nil` becomes `.None`.
-
-## StringLiteralConvertible and Regular Expressions
-
-Swift literal convertibles can be used to provide convenient shorthand initializers for custom objects.
-
-Recall our [`Regex`](http://nshipster.com/swift-operators/) example from last week:
-
-```swift
-struct Regex {
- let pattern: String
- let options: NSRegularExpressionOptions!
-
- private var matcher: NSRegularExpression {
- return NSRegularExpression(pattern: self.pattern, options: self.options, error: nil)
- }
-
- init(pattern: String, options: NSRegularExpressionOptions = nil) {
- self.pattern = pattern
- self.options = options
- }
-
- func match(string: String, options: NSMatchingOptions = nil) -> Bool {
- return self.matcher.numberOfMatchesInString(string, options: options, range: NSMakeRange(0, string.utf16Count)) != 0
- }
-}
-```
-
-Developers coming from a Ruby or Perl background may be disappointed by Swift's lack of support for regular expression literals, but this can be retcon'd in using the `StringLiteralConvertible` protocol:
-
-```swift
-extension Regex: StringLiteralConvertible {
- typealias ExtendedGraphemeClusterLiteralType = StringLiteralType
-
- init(unicodeScalarLiteral value: UnicodeScalarLiteralType) {
- self.pattern = "\(value)"
- }
-
- init(extendedGraphemeClusterLiteral value: ExtendedGraphemeClusterLiteralType) {
- self.pattern = value
- }
-
- init(stringLiteral value: StringLiteralType) {
- self.pattern = value
- }
-}
-```
-
-> `StringLiteralConvertible` itself inherits from the `ExtendedGraphemeClusterLiteralConvertible` protocol, which in turn inherits from `UnicodeScalarLiteralConvertible`. `ExtendedGraphemeClusterLiteralType` is an internal type representing a `String` of length 1, while `UnicodeScalarLiteralType` is an internal type representing a `Character`. In order to implement the required `init`s, `ExtendedGraphemeClusterLiteralType` and `UnicodeScalarLiteralType` can be `typealias`'d to `StringLiteralType` and `Character`, respectively.
-
-Now, we can do this:
-
-```swift
-let string: String = "foo bar baz"
-let regex: Regex = "foo"
-
-regex.match(string) // true
-```
-
-...or more simply:
-
-```swift
-"foo".match(string) // true
-```
-
-Combined with the [custom operator `=~`](http://nshipster.com/swift-operators), this can be made even more idiomatic:
-
-```swift
-"foo bar baz" =~ "foo" // true
-```
-
----
-
-Some might bemoan this as the end of comprehensibility, while others will see this merely as filling in one of the missing parts of this new language.
-
-It's all just a matter of what you're used to, and whether you think a developer is entitled to add features to a language in order for it to better suit their purposes.
-
-> Either way, I hope we can all agree that this language feature is _interesting_, and worthy of further investigation. So in that spirit, let's venture forth and illustrate a few more use cases.
-
----
-
-## ArrayLiteralConvertible and Sets
-
-For a language with such a deep regard for immutability and safety, it's somewhat odd that there is no built-in support for sets in the standard library.
-
-Arrays are nice and all, but the `O(1)` lookup and idempotence of sets... _\*whistful sigh\*_
-
-So here's a simple example of how `Set` might be implemented in Swift, using the built-in `Dictionary` type:
-
-```swift
-struct Set {
- typealias Index = T
- private var dictionary: [T: Bool] = [:]
-
- var count: Int {
- return self.dictionary.count
- }
-
- var isEmpty: Bool {
- return self.dictionary.isEmpty
- }
-
- func contains(element: T) -> Bool {
- return self.dictionary[element] ?? false
- }
-
- mutating func put(element: T) {
- self.dictionary[element] = true
- }
-
- mutating func remove(element: T) -> Bool {
- if self.contains(element) {
- self.dictionary.removeValueForKey(element)
- return true
- } else {
- return false
- }
- }
-}
-```
-
-> A real, standard library-calibre implementation of `Set` would involve a _lot_ more Swift-isms, like generators, sequences, and all manner of miscellaneous protocols. It's enough to write an entirely separate article about.
-
-Of course, a standard collection class is only as useful as it is convenient to use. `NSSet` wasn't so lucky to receive the first-class treatment when array and dictionary literal syntax was introduced with the [Apple LLVM Compiler 4.0](http://clang.llvm.org/docs/ObjectiveCLiterals.html), but we can right the wrongs of the past with the `ArrayLiteralConvertible` protocol:
-
-```swift
-protocol ArrayLiteralConvertible {
- typealias Element
- init(arrayLiteral elements: Element...)
-}
-```
-
-Extending `Set` to adopt this protocol is relatively straightforward:
-
-```swift
-extension Set: ArrayLiteralConvertible {
- public init(arrayLiteral elements: T...) {
- for element in elements {
- put(element)
- }
- }
-}
-```
-
-But that's all it takes to achieve our desired results:
-
-```swift
-let set: Set = [1,2,3]
-set.contains(1) // true
-set.count // 3
-```
-
-> This example does, however, highlight a legitimate concern for literal convertibles: **type inference ambiguity**. Because of the significant API overlap between collection classes like `Array` and `Set`, one could ostensibly write code that would behave differently depending on how the type was resolved (e.g. set addition is idempotent, whereas arrays accumulate, so the count after adding two equivalent elements would differ)
-
-## StringLiteralConvertible and URLs
-
-Alright, one last example creative use of literal convertibles: URL literals.
-
-`NSURL` is the fiat currency of the URL Loading System, with the nice feature of introspection of its component parts according to [RFC 2396](https://www.ietf.org/rfc/rfc2396.txt). Unfortunately, it's so inconvenient to instantiate, that third-party framework authors often decide to ditch them in favor of worse-but-more-convenient strings for method parameters.
-
-With a simple extension on `NSURL`, one can get the best of both worlds:
-
-```swift
-extension NSURL: StringLiteralConvertible {
- public class func convertFromExtendedGraphemeClusterLiteral(value: String) -> Self {
- return self(string: value)
- }
-
- public class func convertFromStringLiteral(value: String) -> Self {
- return self(string: value)
- }
-}
-```
-
-One neat feature of literal convertibles is that the type inference works even without a variable declaration:
-
-```swift
-"http://nshipster.com/".host // nshipster.com
-```
-
-* * *
-
-As a community, it's up to us to decide what capabilities of Swift are features and what are bugs. We'll be the ones to distinguish pattern from anti-pattern; convention from red flag.
-
-So it's unclear, at the present moment, how things like literal convertibles, custom operators, and all of the other capabilities of Swift will be reconciled. This publication has, at times, been more or less prescriptive on how things should be, but in this case, that's not the case here.
-
-All there is to be done is to experiment and learn.
diff --git a/2014-08-18-swift-literals.md b/2014-08-18-swift-literals.md
new file mode 100644
index 00000000..d862425d
--- /dev/null
+++ b/2014-08-18-swift-literals.md
@@ -0,0 +1,412 @@
+---
+title: Swift Literals
+author: Mattt
+category: Swift
+tags: swift
+excerpt: >-
+ Literals are representations of values in source code.
+ The different kinds of literals that Swift provides —
+ and how it makes them available —
+ has a profound impact on how we write and think about code.
+revisions:
+ "2014-08-18": Original publication
+ "2018-08-22": Updated for Swift 4.2
+status:
+ swift: 4.2
+---
+
+In 1911,
+linguist [Franz Boas](https://en.wikipedia.org/wiki/Franz_Boas)
+observed that speakers of
+[Eskimo–Aleut languages](https://en.wikipedia.org/wiki/Eskimo–Aleut_languages)
+used different words to distinguish falling snowflakes from snow on the ground.
+By comparison, English speakers typically refer to both as "snow,"
+but create a similar distinction between raindrops and puddles.
+
+Over time,
+this simple empirical observation
+has warped into an awful cliché that
+"Eskimos _\[sic\]_ have 50 different words for snow" ---
+which is unfortunate,
+because Boas' original observation was empirical,
+and the resulting weak claim of linguistic relativity is uncontroversial:
+languages divide semantic concepts into separate words
+in ways that may (and often do) differ from one another.
+Whether that's more an accident of history
+or reflective of some deeper truth about a culture is unclear,
+and subject for further debate.
+
+It's in this framing that you're invited to consider
+how the different kinds of literals in Swift
+shape the way we reason about code.
+
+## Standard Literals
+
+A literal is a representation of a value in source code,
+such as a number or a string.
+
+Swift provides the following kinds of literals:
+
+| Name | Default Inferred Type | Examples |
+| ------------------------- | --------------------- | --------------------------------- |
+| Integer | `Int` | `123`, `0b1010`, `0o644`, `0xFF`, |
+| Floating-Point | `Double` | `3.14`, `6.02e23`, `0xAp-2` |
+| String | `String` | `"Hello"`, `""" . . . """` |
+| Extended Grapheme Cluster | `Character` | `"A"`, `"é"`, `"🇺🇸"` |
+| Unicode Scalar | `Unicode.Scalar` | `"A"`, `"´"`, `"\u{1F1FA}"` |
+| Boolean | `Bool` | `true`, `false` |
+| Nil | `Optional` | `nil` |
+| Array | `Array` | `[1, 2, 3]` |
+| Dictionary | `Dictionary` | `["a": 1, "b": 2]` |
+
+The most important thing to understand about literals in Swift
+is that they specify a value, but not a definite type.
+
+When the compiler encounters a literal,
+it attempts to infer the type automatically.
+It does this by looking for each type
+that could be initialized by that kind of literal,
+and narrowing it down based on any other constraints.
+
+If no type can be inferred,
+Swift initializes the default type for that kind of literal ---
+`Int` for an integer literal,
+`String` for a string literal,
+and so on.
+
+```swift
+57 // Integer literal
+"Hello" // String literal
+```
+
+In the case of `nil` literals,
+the type can never be inferred automatically
+and therefore must be declared.
+
+```swift
+nil // ! cannot infer type
+nil as String? // Optional.none
+```
+
+For array and dictionary literals,
+the associated types for the collection
+are inferred based on its contents.
+However, inferring types for large or nested collections
+is a complex operation and
+may significantly increase the amount of time it takes to compile your code.
+You can keep things snappy by adding an explicit type in your declaration.
+
+```swift
+// Explicit type in the declaration
+// prevents expensive type inference during compilation
+let dictionary: [String: [Int]] = [
+ "a": [1, 2],
+ "b": [3, 4],
+ "c": [5, 6],
+ <#...#>
+]
+```
+
+## Playground Literals
+
+In addition to the standard literals listed above,
+there are a few additional literal types for code in Playgrounds:
+
+| Name | Default Inferred Type | Examples |
+| ----- | --------------------- | ---------------------------------------------------- |
+| Color | `NSColor` / `UIColor` | `#colorLiteral(red: 1, green: 0, blue: 1, alpha: 1)` |
+| Image | `NSImage` / `UIImage` | `#imageLiteral(resourceName: "icon")` |
+| File | `URL` | `#fileLiteral(resourceName: "articles.json")` |
+
+In Xcode or Swift Playgrounds on the iPad,
+these octothorpe-prefixed literal expressions
+are automatically replaced by an interactive control
+that provides a visual representation of the referenced color, image, or file.
+
+```swift
+// Code
+#colorLiteral(red: 0.7477839589, green: 0.5598286986, blue: 0.4095913172, alpha: 1)
+
+// Rendering
+🏽
+```
+
+{% asset color-literal-picker.png alt="Swift Playgrounds Color Literal Picker"%}
+
+This control also makes it easy for new values to be chosen:
+instead of entering RGBA values or file paths,
+you're presented with a color picker or file selector.
+
+---
+
+Most programming languages have literals for
+Boolean values, numbers, and strings,
+and many have literals for arrays, dictionaries, and regular expressions.
+
+Literals are so ingrained in a developer's mental model of programming
+that most of us don't actively consider what the compiler is actually doing.
+
+Having a shorthand for these essential building blocks
+makes code easier to both read and write.
+
+## How Literals Work
+
+Literals are like words:
+their meaning can change depending on the surrounding context.
+
+```swift
+["h", "e", "l", "l", "o"] // Array
+["h" as Character, "e", "l", "l", "o"] // Array
+["h", "e", "l", "l", "o"] as Set
+```
+
+In the example above,
+we see that an array literal containing string literals
+is initialized to an array of strings by default.
+However, if we explicitly cast the first array element as `Character`,
+the literal is initialized as an array of characters.
+Alternatively, we could cast the entire expression as `Set`
+to initialize a set of characters.
+
+_How does this work?_
+
+In Swift,
+the compiler decides how to initialize literals
+by looking at all the visible types that implement the corresponding
+literal expression protocol.
+
+| Literal | Protocol |
+| ------------------------- | --------------------------------------------- |
+| Integer | `ExpressibleByIntegerLiteral` |
+| Floating-Point | `ExpressibleByFloatLiteral` |
+| String | `ExpressibleByStringLiteral` |
+| Extended Grapheme Cluster | `ExpressibleByExtendedGraphemeClusterLiteral` |
+| Unicode Scalar | `ExpressibleByUnicodeScalarLiteral` |
+| Boolean | `ExpressibleByBooleanLiteral` |
+| Nil | `ExpressibleByNilLiteral` |
+| Array | `ExpressibleByArrayLiteral` |
+| Dictionary | `ExpressibleByDictionaryLiteral` |
+
+To conform to a protocol,
+a type must implement its required initializer.
+For example,
+the `ExpressibleByIntegerLiteral` protocol
+requires `init(integerLiteral:)`.
+
+What's really great about this approach
+is that it lets you add literal initialization
+for your own custom types.
+
+## Supporting Literal Initialization for Custom Types
+
+Supporting initialization by literals when appropriate
+can significantly improve the ergonomics of custom types,
+making them feel like they're built-in.
+
+For example,
+if you wanted to support
+[fuzzy logic](https://en.wikipedia.org/wiki/Fuzzy_logic),
+in addition to standard Boolean fare,
+you might implement a `Fuzzy` type like the following:
+
+```swift
+struct Fuzzy: Equatable {
+ var value: Double
+
+ init(_ value: Double) {
+ precondition(value >= 0.0 && value <= 1.0)
+ self.value = value
+ }
+}
+```
+
+A `Fuzzy` value represents a truth value that ranges between
+completely true and completely false
+over the numeric range 0 to 1 (inclusive).
+That is, a value of 1 means completely true,
+0.8 means mostly true,
+and 0.1 means mostly false.
+
+In order to work more conveniently with standard Boolean logic,
+we can extend `Fuzzy` to adopt the `ExpressibleByBooleanLiteral` protocol.
+
+```swift
+extension Fuzzy: ExpressibleByBooleanLiteral {
+ init(booleanLiteral value: Bool) {
+ self.init(value ? 1.0 : 0.0)
+ }
+}
+```
+
+> In practice,
+> there aren't many situations in which it'd be appropriate
+> for a type to be initialized using Boolean literals.
+> Support for string, integer, and floating-point literals are much more common.
+
+Doing so doesn't change the default meaning of `true` or `false`.
+We don't have to worry about existing code breaking
+just because we introduced the concept of half-truths to our code base
+("_view did appear animated... maybe?_").
+The only situations in which `true` or `false` initialize a `Fuzzy` value
+would be when the compiler could infer the type to be `Fuzzy`:
+
+```swift
+true is Bool // true
+true is Fuzzy // false
+
+(true as Fuzzy) is Fuzzy // true
+(false as Fuzzy).value // 0.0
+```
+
+Because `Fuzzy` is initialized with a single `Double` value,
+it's reasonable to allow values to be initialized with
+floating-point literals as well.
+It's hard to think of any situations in which
+a type would support floating-point literals but not integer literals,
+so we should do that too
+(however, the converse isn't true;
+there are plenty of types that work with integer but not floating point numbers).
+
+```swift
+extension Fuzzy: ExpressibleByIntegerLiteral {
+ init(integerLiteral value: Int) {
+ self.init(Double(value))
+ }
+}
+
+extension Fuzzy: ExpressibleByFloatLiteral {
+ init(floatLiteral value: Double) {
+ self.init(value)
+ }
+}
+```
+
+With these protocols adopted,
+the `Fuzzy` type now looks and feels like
+a _bona fide_ member of Swift standard library.
+
+```swift
+let completelyTrue: Fuzzy = true
+let mostlyTrue: Fuzzy = 0.8
+let mostlyFalse: Fuzzy = 0.1
+```
+
+(Now the only thing left to do is implement the standard logical operators!)
+
+If convenience and developer productivity is something you want to optimize for,
+you should consider implementing whichever literal protocols
+are appropriate for your custom types.
+
+## Future Developments
+
+Literals are an active topic of discussion
+for the future of the language.
+Looking forward to Swift 5,
+there are a number of current proposals
+that could have terrific implications for how we write code.
+
+### Raw String Literals
+
+At the time of writing,
+[Swift Evolution proposal 0200](https://github.com/apple/swift-evolution/blob/master/proposals/0200-raw-string-escaping.md)
+is in active review.
+If it's accepted,
+future versions of Swift will support "raw" strings,
+or string literals that ignores escape sequences.
+
+From the proposal:
+
+> Our design adds customizable string delimiters.
+> You may pad a string literal with one or more
+> `#` (pound, Number Sign, U+0023) characters [...]
+> The number of pound signs at the start of the string
+> (in these examples, zero, one, and four)
+> must match the number of pound signs at the end of the string.
+
+```swift
+"This is a Swift string literal"
+
+#"This is also a Swift string literal"#
+
+####"So is this"####
+```
+
+This proposal comes as a natural extension of the new multi-line string literals
+added in Swift 4
+([SE-0165](https://github.com/apple/swift-evolution/blob/master/proposals/0168-multi-line-string-literals.md)),
+and would make it even easier to do work with data formats like JSON and XML.
+
+If nothing else,
+adoption of this proposal
+could remove the largest obstacle to using Swift on Windows:
+dealing with file paths like `C:\Windows\All Users\Application Data`.
+
+### Literal Initialization Via Coercion
+
+Another recent proposal,
+[SE-0213: Literal initialization via coercion](https://github.com/apple/swift-evolution/blob/master/proposals/0213-literal-init-via-coercion.md)
+is already implemented for Swift 5.
+
+From the proposal:
+
+> `T(literal)` should construct `T`
+> using the appropriate literal protocol if possible.
+
+> Currently types conforming to literal protocols
+> are type-checked using regular initializer rules,
+> which means that for expressions like `UInt32(42)`
+> the type-checker is going to look up a set of available initializer choices
+> and attempt them one-by-one trying to deduce the best solution.
+
+In Swift 4.2,
+initializing a `UInt64` with its maximum value
+results in a compile-time overflow
+because the compiler first tries to initialize an `Int` with the literal value.
+
+```swift
+UInt64(0xffff_ffff_ffff_ffff) // overflows in Swift 4.2
+```
+
+Starting in Swift 5,
+not only will this expression compile successfully,
+but it'll do so a little bit faster, too.
+
+---
+
+The words available to a language speaker
+influence not only what they say,
+but how they think as well.
+In the same way,
+the individual parts of a programming language
+hold considerable influence over how a developer works.
+
+The way Swift carves up the semantic space of values
+makes it different from languages that don't,
+for example,
+distinguish between integers and floating points
+or have separate concepts for strings, characters, and Unicode scalars.
+So it's no coincidence that when we write Swift code,
+we often think about numbers and strings at a lower level
+than if we were hacking away in, say, JavaScript.
+
+Along the same lines,
+Swift's current lack of distinction
+between string literals and regular expressions
+contributes to the relative lack of regex usage compared to other
+scripting languages.
+
+That's not to say that having or lacking certain words
+makes it impossible to express certain ideas ---
+just a bit fuzzier.
+We can understand "untranslatable" words like
+["Saudade"](https://en.wikipedia.org/wiki/Saudade) in Portuguese,
+["Han"](https://en.wikipedia.org/wiki/Han_%28cultural%29) in Korean, or
+["Weltschmerz"](https://en.wikipedia.org/wiki/Weltschmerz) in German.
+
+We're all human.
+We all understand pain.
+
+By allowing any type to support literal initialization,
+Swift invites us to be part of the greater conversation.
+Take advantage of this
+and make your own code feel like a natural extension of the standard library.
diff --git a/2014-08-24-wkwebkit.md b/2014-08-24-wkwebkit.md
deleted file mode 100644
index fe35e820..00000000
--- a/2014-08-24-wkwebkit.md
+++ /dev/null
@@ -1,227 +0,0 @@
----
-title: WKWebView
-author: Mattt Thompson
-category: Cocoa
-excerpt: "iOS has a complicated relationship with the web. And it goes back to the very inception of the platform nearly a decade ago."
-status:
- swift: 2.0
- reviewed: September 8, 2015
----
-
-iOS has a complicated relationship with the web. And it goes back to the very inception of the platform nearly a decade ago.
-
-It's difficult to appreciate just how differently the first iPhone could have turned out. The iconic touchscreen device we know and love today was just one option on the table. Early prototypes explored the use of a physical keyboard and a touch screen + stylus combo, with screen dimensions going up to 5" x 7". Even the iPod click wheel was a serious contender for a time.
-
-But perhaps the most significant early decision to be made involved software, not hardware.
-
-How should the iPhone run software? Apps, like on OSX, or as web pages, using Safari? That choice to fork OS X and build iPhoneOS had widespread implications, and remains a contentious decision to this day.
-
-Consider this infamous line from Steve Jobs' WWDC 2007 keynote:
-
-> The full Safari engine is inside of iPhone. And so, you can write amazing Web 2.0 and Ajax apps that look exactly and behave exactly like apps on the iPhone. And these apps can integrate perfectly with iPhone services. They can make a call, they can send an email, they can look up a location on Google Maps.
-
-The web has always been a second-class citizen on iOS _(which is ironic, since the iPhone is largely responsible for the mobile web existing as it does today)_. `UIWebView` is massive and clunky and leaks memory. It lags behind Mobile Safari, which has the benefit of the Nitro JavaScript engine.
-
-However, all of this has changed with the introduction of `WKWebView` and the rest of the `WebKit` framework.
-
-* * *
-
-`WKWebView` is the centerpiece of the modern WebKit API introduced in iOS 8 & OS X Yosemite. It replaces `UIWebView` in UIKit and `WebView` in AppKit, offering a consistent API across the two platforms.
-
-Boasting responsive 60fps scrolling, built-in gestures, streamlined communication between app and webpage, and the same JavaScript engine as Safari, `WKWebView` is one of the most significant announcements to come out of WWDC 2014.
-
-What was a single class and protocol with `UIWebView` & `UIWebViewDelegate` has been factored out into 14 classes and 3 protocols in WKWebKit. Don't be alarmed by the huge jump in complexity, though—this new architecture allows for a ton of new features:
-
-## WKWebKit Framework
-
-### Classes
-
-> - `WKBackForwardList`: A list of webpages previously visited in a web view that can be reached by going back or forward.
-> - `WKBackForwardListItem`: Represents a webpage in the back-forward list of a web view.
-> - `WKFrameInfo`: Contains information about a frame on a webpage.
-> - `WKNavigation`: Contains information for tracking the loading progress of a webpage.
-> - `WKNavigationAction`: Contains information about an action that may cause a navigation, used for making policy decisions.
-> - `WKNavigationResponse`: Contains information about a navigation response, used for making policy decisions.
-> - `WKPreferences`: Encapsulates the preference settings for a web view.
-> - `WKProcessPool`: Represents a pool of Web Content processes.
-> - `WKUserContentController`: Provides a way for JavaScript to post messages and inject user scripts to a web view.
-> - `WKScriptMessage`: Contains information about a message sent from a webpage.
-> - `WKUserScript`: Represents a script that can be injected into a webpage.
-> - `WKWebViewConfiguration`: A collection of properties with which to initialize a web view.
-> - `WKWindowFeatures`: Specifies optional attributes for the containing window when a new web view is requested.
-
-### Protocols
-
-> - `WKNavigationDelegate`: Provides methods for tracking the progress of main frame navigations and for deciding load policy for main frame and subframe navigations.
-> - `WKScriptMessageHandler`: Provides a method for receiving messages from JavaScript running in a webpage.
-> - `WKUIDelegate`: Provides methods for presenting native user interface elements on behalf of a webpage.
-
-## API Diff Between `UIWebView` & `WKWebView`
-
-`WKWebView` inherits much of the same programming interface as `UIWebView`, making it convenient for apps to migrate to WKWebKit (and conditionally compile as necessary while iOS 8 gains widespread adoption).
-
-For anyone interested in the specifics, here's a comparison of the APIs of each class:
-
-| UIWebView | WKWebView |
-|-----------|-----------|
-| `var scrollView: UIScrollView { get }` | `var scrollView: UIScrollView { get }` |
-| | `var configuration: WKWebViewConfiguration { get }` |
-| `var delegate: UIWebViewDelegate?` | `var UIDelegate: WKUIDelegate?` |
-| | `var navigationDelegate: WKNavigationDelegate?` |
-| | `var backForwardList: WKBackForwardList { get }` |
-
-### Loading
-
-| UIWebView | WKWebView |
-|-----------|-----------|
-| `func loadRequest(request: NSURLRequest)` | `func loadRequest(request: NSURLRequest) -> WKNavigation?` |
-| `func loadHTMLString(string: String, baseURL: NSURL?)` | `func loadHTMLString(string: String, baseURL: NSURL) -> WKNavigation?` |
-| `func loadData(data: NSData, MIMEType: String, textEncodingName: String, baseURL: NSURL)` | |
-| | `var estimatedProgress: Double { get }` |
-| | `var hasOnlySecureContent: Bool { get }` |
-| `func reload()` | `func reload() -> WKNavigation?` |
-| | `func reloadFromOrigin() -> WKNavigation?` |
-| `func stopLoading()` | `func stopLoading()` |
-| `var request: NSURLRequest? { get }` | |
-| | `var URL: NSURL? { get }` |
-| | `var title: String? { get }` |
-
-### History
-
-| UIWebView | WKWebView |
-|-----------|-----------|
-| | `func goToBackForwardListItem(item: WKBackForwardListItem) -> WKNavigation?` |
-| `func goBack()` | `func goBack() -> WKNavigation?` |
-| `func goForward()` | `func goForward() -> WKNavigation?` |
-| `var canGoBack: Bool { get }` | `var canGoBack: Bool { get }` |
-| `var canGoForward: Bool { get }` | `var canGoForward: Bool { get }` |
-| `var loading: Bool { get }` | `var loading: Bool { get }` |
-
-### Javascript Evaluation
-
-| UIWebView | WKWebView |
-|-----------|-----------|
-| `func stringByEvaluatingJavaScriptFromString(script: String) -> String` | |
-| | `func evaluateJavaScript(javaScriptString: String, completionHandler: ((AnyObject?, NSError?) -> Void)?)` |
-
-### Miscellaneous
-
-| UIWebView | WKWebView |
-|-----------|-----------|
-| `var keyboardDisplayRequiresUserAction: Bool` | |
-| `var scalesPageToFit: Bool` | |
-| | `var allowsBackForwardNavigationGestures: Bool` |
-
-### Pagination
-
-`WKWebView` currently lacks equivalent APIs for paginating content.
-
-- `var paginationMode: UIWebPaginationMode`
-- `var paginationBreakingMode: UIWebPaginationBreakingMode`
-- `var pageLength: CGFloat`
-- `var gapBetweenPages: CGFloat`
-- `var pageCount: Int { get }`
-
-### Refactored into `WKWebViewConfiguration`
-
-The following properties on `UIWebView` have been factored into a separate configuration object, which is passed into the initializer for `WKWebView`:
-
-- `var allowsInlineMediaPlayback: Bool`
-- `var mediaPlaybackRequiresUserAction: Bool`
-- `var mediaPlaybackAllowsAirPlay: Bool`
-- `var suppressesIncrementalRendering: Bool`
-
----
-
-## JavaScript ↔︎ Swift Communication
-
-One of the major improvements over `UIWebView` is how interaction and data can be passed back and forth between an app and its web content.
-
-### Injecting Behavior with User Scripts
-
-`WKUserScript` allows JavaScript behavior to be injected at the start or end of document load. This powerful feature allows for web content to be manipulated in a safe and consistent way across page requests.
-
-As a simple example, here's how a user script can be injected to change the background color of a web page:
-
-```swift
-let source = "document.body.style.background = \"#777\";"
-let userScript = WKUserScript(source: source, injectionTime: .AtDocumentEnd, forMainFrameOnly: true)
-
-let userContentController = WKUserContentController()
-userContentController.addUserScript(userScript)
-
-let configuration = WKWebViewConfiguration()
-configuration.userContentController = userContentController
-self.webView = WKWebView(frame: self.view.bounds, configuration: configuration)
-```
-
-`WKUserScript` objects are initialized with JavaScript source, as well as whether it should be injected at the start or end of loading the page, and whether this behavior should be used for all frames, or just the main frame. The user script is then added to a `WKUserContentController`, which is set on the `WKWebViewConfiguration` object passed into the initializer for `WKWebView`.
-
-This example could easily be extended to perform more significant modifications to the page, such as removing advertisements, hiding comments, or maybe [changing all occurrences of the phrase "the cloud" to "my butt"](https://github.com/panicsteve/cloud-to-butt).
-
-### Message Handlers
-
-Communication from web to app has improved significantly as well, with message handlers.
-
-Just like `console.log` prints out information to the [Safari Web Inspector](https://developer.apple.com/library/safari/documentation/AppleApplications/Conceptual/Safari_Developer_Guide/Introduction/Introduction.html) debug console, information from a web page can be passed back to the app by invoking:
-
-```javascript
-window.webkit.messageHandlers.{NAME}.postMessage()
-```
-
-> What's really great about this API is that JavaScript objects are _automatically serialized_ into native Objective-C or Swift objects.
-
-The name of the handler is configured in `addScriptMessageHandler()`, which registers a handler conforming to the `WKScriptMessageHandler` protocol:
-
-```swift
-class NotificationScriptMessageHandler: NSObject, WKScriptMessageHandler {
- func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage!) {
- print(message.body)
- }
-}
-
-let userContentController = WKUserContentController()
-let handler = NotificationScriptMessageHandler()
-userContentController.addScriptMessageHandler(handler, name: "notification")
-```
-
-Now, when a notification comes into the app, such as to notify the creation of a new object on the page, that information can be passed with:
-
-```javascript
-window.webkit.messageHandlers.notification.postMessage({body: "..."});
-```
-
-> Add User Scripts to create hooks for webpage events that use Message Handlers to communicate status back to the app.
-
-The same approach can be used to scrape information from the page for display or analysis within the app.
-
-For example, if someone were to build a browser specifically for NSHipster.com, it could have a button that listed related articles in a popover:
-
-```javascript
-// document.location.href == "http://nshipster.com/webkit"
-function getRelatedArticles() {
- var related = [];
- var elements = document.getElementById("related").getElementsByTagName("a");
- for (i = 0; i < elements.length; i++) {
- var a = elements[i];
- related.push({href: a.href, title: a.title});
- }
-
- window.webkit.messageHandlers.related.postMessage({articles: related});
-}
-```
-
-```swift
-let js = "getRelatedArticles();"
-self.webView?.evaluateJavaScript(js) { (_, error) in
- print(error)
-}
-
-// Get results in previously-registered message handler
-```
-
-* * *
-
-If your app is little more than a thin container around web content, `WKWebView` is a game-changer. All of that performance and compatibility that you've longed for is finally available. It's everything you might have hoped for.
-
-If you're more of a native controls purist, you may be surprised at the power and flexibility afforded by the new technologies in iOS 8. It's a dirty secret that some stock apps like Messages use WebKit to render tricky content. The fact that you probably haven't noticed should be an indicator that web views actually have a place in app development best practices.
diff --git a/2014-08-24-wkwebview.md b/2014-08-24-wkwebview.md
new file mode 100644
index 00000000..2f3ea09e
--- /dev/null
+++ b/2014-08-24-wkwebview.md
@@ -0,0 +1,386 @@
+---
+title: WKWebView
+author: Mattt
+category: Cocoa
+excerpt: iOS has a complicated relationship with the web.
+ And it goes back to the very inception of the platform over a decade ago.
+revisions:
+ "2014-08-24": Original publication
+ "2018-07-25": Updated for iOS 12 and macOS Mojave
+status:
+ swift: 4.2
+ reviewed: July 25, 2018
+---
+
+iOS has a complicated relationship with the web.
+And it goes back to the very inception of the platform over a decade ago.
+
+Although the design of the first iPhone seems like a foregone conclusion today,
+the iconic touchscreen device we know and love today
+was just one option on the table at the time.
+Early prototypes explored the use of a physical keyboard
+and a touchscreen + stylus combo,
+with screen dimensions going up to 5×7".
+Even the iPod click wheel was a serious contender for a time.
+
+But perhaps the most significant early decision to be made involved software,
+not hardware.
+
+How should the iPhone run software?
+Apps, like on macOS?
+Or as web pages, using Safari?
+That choice to fork macOS and build iPhoneOS had widespread implications
+and remains a contentious decision to this day.
+
+Consider this infamous line from Steve Jobs' WWDC 2007 keynote:
+
+> The full Safari engine is inside of iPhone.
+> And so, you can write amazing Web 2.0 and Ajax apps
+> that look exactly and behave exactly like apps on the iPhone.
+> And these apps can integrate perfectly with iPhone services.
+> They can make a call, they can send an email,
+> they can look up a location on Google Maps.
+
+The web had long been a second-class citizen on iOS,
+which is ironic since the iPhone is largely responsible
+for the mobile web as it exists today.
+`UIWebView` was massive and clunky and leaked memory like a sieve.
+It lagged behind Mobile Safari,
+unable to take advantage of its faster JavaScript and rendering engines.
+
+However, all of this changed with the introduction of `WKWebView`
+and the rest of the `WebKit` framework.
+
+---
+
+`WKWebView` is the centerpiece of the modern WebKit API
+introduced in iOS 8 & macOS Yosemite.
+It replaces `UIWebView` in UIKit and `WebView` in AppKit,
+offering a consistent API across the two platforms.
+
+Boasting responsive 60fps scrolling,
+built-in gestures,
+streamlined communication between app and webpage,
+and the same JavaScript engine as Safari,
+`WKWebView` was one of the most significant announcements at WWDC 2014.
+
+What was once a single class and protocol with `UIWebView` & `UIWebViewDelegate`
+has been factored out into 14 classes and 3 protocols in the WebKit framework.
+Don't be alarmed by the huge jump in complexity, though ---
+this new architecture is much cleaner,
+and allows for a ton of new features.
+
+## Migrating from UIWebView / WebView to WKWebView
+
+`WKWebView` has been the preferred API since iOS 8.
+But if your app _still_ hasn't made the switch,
+be advised that
+**`UIWebView` and `WebView` are formally deprecated
+in iOS 12 and macOS Mojave**,
+and you should update to `WKWebView` as soon as possible.
+
+To help make that transition,
+here's a comparison of the APIs of `UIWebView` and `WKWebView`:
+
+| UIWebView | WKWebView |
+| -------------------------------------- | --------------------------------------------------- |
+| `var scrollView: UIScrollView { get }` | `var scrollView: UIScrollView { get }` |
+| | `var configuration: WKWebViewConfiguration { get }` |
+| `var delegate: UIWebViewDelegate?` | `var UIDelegate: WKUIDelegate?` |
+| | `var navigationDelegate: WKNavigationDelegate?` |
+| | `var backForwardList: WKBackForwardList { get }` |
+
+### Loading
+
+| UIWebView | WKWebView |
+| ------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
+| `func loadRequest(request: URLRequest)` | `func load(_ request: URLRequest) -> WKNavigation?` |
+| `func loadHTMLString(string: String, baseURL: URL?)` | `func loadHTMLString(_: String, baseURL: URL?) -> WKNavigation?` |
+| `func loadData(_ data: Data, mimeType: String, characterEncodingName: String, baseURL: URL) -> WKNavigation?` | |
+| | `var estimatedProgress: Double { get }` |
+| | `var hasOnlySecureContent: Bool { get }` |
+| `func reload()` | `func reload() -> WKNavigation?` |
+| | `func reloadFromOrigin(Any?) -> WKNavigation?` |
+| `func stopLoading()` | `func stopLoading()` |
+| `var request: URLRequest? { get }` | |
+| | `var URL: URL? { get }` |
+| | `var title: String? { get }` |
+
+### History
+
+| UIWebView | WKWebView |
+| -------------------------------- | ---------------------------------------------------------------------------- |
+| | `func goToBackForwardListItem(item: WKBackForwardListItem) -> WKNavigation?` |
+| `func goBack()` | `func goBack() -> WKNavigation?` |
+| `func goForward()` | `func goForward() -> WKNavigation?` |
+| `var canGoBack: Bool { get }` | `var canGoBack: Bool { get }` |
+| `var canGoForward: Bool { get }` | `var canGoForward: Bool { get }` |
+| `var loading: Bool { get }` | `var loading: Bool { get }` |
+
+### Javascript Evaluation
+
+| UIWebView | WKWebView |
+| ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
+| `func stringByEvaluatingJavaScriptFromString(script: String) -> String` | |
+| | `func evaluateJavaScript(_ javaScriptString: String, completionHandler: ((AnyObject?, NSError?) -> Void)?)` |
+
+### Miscellaneous
+
+| UIWebView | WKWebView |
+| --------------------------------------------- | ----------------------------------------------- |
+| `var keyboardDisplayRequiresUserAction: Bool` | |
+| `var scalesPageToFit: Bool` | |
+| | `var allowsBackForwardNavigationGestures: Bool` |
+
+### Pagination
+
+`WKWebView` currently lacks equivalent APIs for paginating content.
+
+- `var paginationMode: UIWebPaginationMode`
+- `var paginationBreakingMode: UIWebPaginationBreakingMode`
+- `var pageLength: CGFloat`
+- `var gapBetweenPages: CGFloat`
+- `var pageCount: Int { get }`
+
+### Refactored into `WKWebViewConfiguration`
+
+The following properties on `UIWebView`
+have been factored into a separate configuration object,
+which is passed into the initializer for `WKWebView`:
+
+- `var allowsInlineMediaPlayback: Bool`
+- `var allowsAirPlayForMediaPlayback: Bool`
+- `var mediaTypesRequiringUserActionForPlayback: WKAudiovisualMediaTypes`
+- `var suppressesIncrementalRendering: Bool`
+
+---
+
+## JavaScript ↔︎ Swift Communication
+
+One of the major improvements over `UIWebView`
+is how interaction and data can be passed back and forth
+between an app and its web content.
+
+### Injecting Behavior with User Scripts
+
+`WKUserScript` allows JavaScript behavior to be injected
+at the start or end of document load.
+This powerful feature allows for web content to be manipulated
+in a safe and consistent way across page requests.
+
+As a simple example,
+here's how a user script can be injected
+to change the background color of a web page:
+
+```swift
+let source = """
+ document.body.style.background = "#777";
+"""
+
+let userScript = WKUserScript(source: source,
+ injectionTime: .atDocumentEnd,
+ forMainFrameOnly: true)
+
+let userContentController = WKUserContentController()
+userContentController.addUserScript(userScript)
+
+let configuration = WKWebViewConfiguration()
+configuration.userContentController = userContentController
+self.webView = WKWebView(frame: self.view.bounds,
+ configuration: configuration)
+```
+
+When you create a `WKUserScript` object,
+you provide JavaScript code to execute,
+specify whether it should be injected
+at the start or end of loading the document,
+and whether the behavior should be used for all frames or just the main frame.
+The user script is then added to a `WKUserContentController`,
+which is set on the `WKWebViewConfiguration` object
+passed into the initializer for `WKWebView`.
+
+This example could easily be extended to perform more significant modifications,
+such as [changing all occurrences of the phrase "the cloud" to "my butt"](https://github.com/panicsteve/cloud-to-butt).
+
+### Message Handlers
+
+Communication from web to app has improved significantly as well,
+with the introduction of message handlers.
+
+Like how `console.log` prints out information to the
+[Safari Web Inspector](https://developer.apple.com/safari/tools/),
+information from a web page can be passed back to the app by invoking:
+
+```javascript
+window.webkit.messageHandlers.<#name#>.postMessage()
+```
+
+> What's really great about this API is that JavaScript objects are
+> _automatically serialized_ into native Objective-C or Swift objects.
+
+The name of the handler is configured in `add(_:name)`,
+which registers a handler conforming to the `WKScriptMessageHandler` protocol:
+
+```swift
+class NotificationScriptMessageHandler: NSObject, WKScriptMessageHandler {
+ func userContentController(_ userContentController: WKUserContentController,
+ didReceive message: WKScriptMessage)
+ {
+ print(message.body)
+ }
+}
+
+let userContentController = WKUserContentController()
+let handler = NotificationScriptMessageHandler()
+userContentController.add(handler, name: "notification")
+```
+
+Now, when a notification comes into the app
+(such as to notify the creation of a new object on the page)
+that information can be passed with:
+
+```javascript
+window.webkit.messageHandlers.notification.postMessage({ body: "..." });
+```
+
+> Add User Scripts to create hooks for webpage events
+> that use Message Handlers to communicate status back to the app.
+
+The same approach can be used to scrape information
+from the page for display
+or analysis within the app.
+
+For example,
+if you wanted to build a browser specifically for NSHipster.com,
+it could have a button that listed related articles in a popover:
+
+```javascript
+// document.location.href == "https://nshipster.com/wkwebview"
+const showRelatedArticles = () => {
+ let related = [];
+ const elements = document.querySelectorAll("#related a");
+ for (const a of elements) {
+ related.push({ href: a.href, title: a.title });
+ }
+
+ window.webkit.messageHandlers.related.postMessage({ articles: related });
+};
+```
+
+```swift
+let js = "showRelatedArticles();"
+self.webView?.evaluateJavaScript(js) { (_, error) in
+ print(error)
+}
+
+// Get results in a previously-registered message handler
+```
+
+## Content Blocking Rules
+
+Though depending on your use case,
+you may be able to skip the hassle of round-trip communication with JavaScript.
+
+As of iOS 11 and macOS High Sierra,
+you can specify declarative content blocking rules for a `WKWebView`,
+just like a
+[Safari Content Blocker app extension](https://developer.apple.com/library/archive/documentation/Extensions/Conceptual/ContentBlockingRules/CreatingRules/CreatingRules.html).
+
+For example,
+if you wanted to [Make Medium Readable Again](https://makemediumreadable.com)
+in your web view,
+you could define the following rules in JSON:
+
+```swift
+let json = """
+[
+ {
+ "trigger": {
+ "if-domain": "*.medium.com"
+ },
+ "action": {
+ "type": "css-display-none",
+ "selector": ".overlay"
+ }
+ }
+]
+"""
+```
+
+Pass these rules to
+`compileContentRuleList(forIdentifier:encodedContentRuleList:completionHandler:)`
+and configure a web view with the resulting content rule list
+in the completion handler:
+
+```swift
+WKContentRuleListStore.default()
+ .compileContentRuleList(forIdentifier: "ContentBlockingRules",
+ encodedContentRuleList: json)
+{ (contentRuleList, error) in
+ guard let contentRuleList = contentRuleList,
+ error == nil else {
+ return
+ }
+
+ let configuration = WKWebViewConfiguration()
+ configuration.userContentController.add(contentRuleList)
+
+ self.webView = WKWebView(frame: self.view.bounds,
+ configuration: configuration)
+}
+```
+
+By declaring rules declaratively,
+WebKit can compile these operations
+into bytecode that can run much more efficiently
+than if you injected JavaScript to do the same thing.
+
+In addition to hiding page elements,
+you can use content blocking rules to
+prevent page resources from loading (like images or scripts),
+strip cookies from requests to the server,
+and force a page to load securely over HTTPS.
+
+## Snapshots
+
+Starting in iOS 11 and macOS High Sierra,
+the WebKit framework provides built-in APIs for taking screenshots of web pages.
+
+To take a picture of your web view's visible viewport
+after everything is finished loading,
+implement the `webView(_:didFinish:)` delegate method
+to call the `takeSnapshot(with:completionHandler:)` method like so:
+
+```swift
+func webView(_ webView: WKWebView,
+ didFinish navigation: WKNavigation!)
+{
+ var snapshotConfiguration = WKSnapshotConfiguration()
+ snapshotConfiguration.snapshotWidth = 1440
+
+ webView.takeSnapshot(with: snapshotConfiguration) { (image, error) in
+ guard let image = image,
+ error == nil else {
+ return
+ }
+
+ <#...#>
+ }
+}
+```
+
+Previously,
+taking screenshots of a web page meant
+messing around with view layers and graphics contexts.
+So a clean, single method option is a welcome addition to the API.
+
+---
+
+`WKWebView` truly makes the web feel like a first-class citizen.
+Even if you consider yourself native purist,
+you may be surprised at the power and flexibility afforded by WebKit.
+
+In fact, many of the apps you use every day rely on WebKit
+to render especially tricky content.
+The fact that you probably haven't noticed should be an indicator
+that web views are consistent with app development best practices.
diff --git a/2014-09-02-swift-default-protocol-implementations.md b/2014-09-02-swift-default-protocol-implementations.md
index 18e6e039..11ba81e5 100644
--- a/2014-09-02-swift-default-protocol-implementations.md
+++ b/2014-09-02-swift-default-protocol-implementations.md
@@ -1,6 +1,6 @@
---
title: Swift Default Protocol Implementations
-author: Mattt Thompson
+author: Mattt
category: Swift
tags: swift
excerpt: "Protocols are the foundation of generics in Swift, but suffer from the lack of a built-in way to provide default implementations for methods. However, there is an interesting workaround in Swift that you probably haven't noticed."
@@ -12,7 +12,7 @@ Swift was announced 3 months ago to the day. For many of us, it was among the mo
First came the infatuation period. We fixated on appearances, on surface-level features like Unicode support (`let 🐶🐮`!) and its new, streamlined syntax. Hell, even its _name_ was objectively better than its predecessor's.
-Within a few weeks, though, after having a chance to go through the Swift manual a few times, we started to understand the full implications of this new multi-paradigm language. All of those folks who had affected the zealotry of functional programmers in order to sound smarter (generics!) learned enough to start backing it up. We finally got the distinction between `class` and `struct` down, and picked up a few tricks like [custom operators](http://nshipster.com/swift-operators/) and [literal convertibles](http://nshipster.com/swift-literal-convertible/) along the way. All of that initial excitement could now be channeled productively into apps and libraries and tutorials.
+Within a few weeks, though, after having a chance to go through the Swift manual a few times, we started to understand the full implications of this new multi-paradigm language. All of those folks who had affected the zealotry of functional programmers in order to sound smarter (generics!) learned enough to start backing it up. We finally got the distinction between `class` and `struct` down, and picked up a few tricks like [custom operators](https://nshipster.com/swift-operators/) and [literal convertibles](https://nshipster.com/swift-literal-convertible/) along the way. All of that initial excitement could now be channeled productively into apps and libraries and tutorials.
Next week's announcement effectively marks the end of the summer for iOS & OS X developers. It's time to reign in our experimentation and start shipping again.
@@ -26,21 +26,21 @@ The underlying mechanism for generics are protocols. A Swift protocol, like an O
> Within the Object-Oriented paradigm, types are often conflated with class identity. **When programming in Swift, though, think about polymorphism through _protocols_ first, before resorting to inheritance.**
-The one major shortcoming of protocols, both in Swift and Objective-C, is the lack of a built-in way to provide default implementations for methods, as one might accomplish in other languages with [mixins](http://en.wikipedia.org/wiki/Mixin) or [traits](http://en.wikipedia.org/wiki/Trait_%28computer_programming%29).
+The one major shortcoming of protocols, both in Swift and Objective-C, is the lack of a built-in way to provide default implementations for methods, as one might accomplish in other languages with [mixins](https://en.wikipedia.org/wiki/Mixin) or [traits](https://en.wikipedia.org/wiki/Trait_%28computer_programming%29).
-...but that's not the end of the story. Swift is a fair bit more [Aspect-Oriented](http://en.wikipedia.org/wiki/Aspect-oriented_programming) than it initially lets on.
+...but that's not the end of the story. Swift is a fair bit more [Aspect-Oriented](https://en.wikipedia.org/wiki/Aspect-oriented_programming) than it initially lets on.
Consider the `Equatable` protocol, used throughout the standard library:
-~~~{swift}
+```swift
protocol Equatable {
- func ==(lhs: Self, rhs: Self) -> Bool
+ static func ==(lhs: Self, rhs: Self) -> Bool
}
-~~~
+```
Given an `Article` `struct` with a `title` and `body` field, implementing `Equatable` is straightforward:
-~~~{swift}
+```swift
struct Article {
let title: String
let body: String
@@ -51,11 +51,11 @@ extension Article: Equatable {}
func ==(lhs: Article, rhs: Article) -> Bool {
return lhs.title == rhs.title && lhs.body == rhs.body
}
-~~~
+```
With everything in place, let's show `Equatable` in action:
-~~~{swift}
+```swift
let title = "Swift Custom Operators: Syntactic Sugar or Menace to Society?"
let body = "..."
@@ -64,7 +64,7 @@ let b = Article(title: title, body: body)
a == b // true
a != b // false
-~~~
+```
Wait... where did `!=` come from?
@@ -72,25 +72,25 @@ Wait... where did `!=` come from?
`!=` is actually drawing its implementation from this function in the standard library:
-~~~{swift}
+```swift
func !=(lhs: T, rhs: T) -> Bool
-~~~
+```
Because `!=` is implemented as a generic function for `Equatable`, any type that conforms to `Equatable`, including `Article`, automatically gets the `!=` operator as well.
If we really wanted to, we could override the implementation of `!=`:
-~~~{swift}
+```swift
func !=(lhs: Article, rhs: Article) -> Bool {
return !(lhs == rhs)
}
-~~~
+```
For equality, it's unlikely that we could offer something more efficient than the negation of the provided `==` check, but this might make sense in other cases. Swift's type inference system allows more specific declarations to trump any generic or implicit candidates.
The standard library uses generic operators all over the place, like for bitwise operations:
-~~~{swift}
+```swift
protocol BitwiseOperationsType {
func &(_: Self, _: Self) -> Self
func |(_: Self, _: Self) -> Self
@@ -99,7 +99,7 @@ protocol BitwiseOperationsType {
class var allZeros: Self { get }
}
-~~~
+```
Implementing functionality in this way significantly reduces the amount of boilerplate code needed to build on top of existing infrastructure.
@@ -109,15 +109,15 @@ However, the aforementioned technique only really works for operators. Providing
Consider a protocol `P` with a method `m()` that takes a single `Int` argument:
-~~~{swift}
+```swift
protocol P {
func m(arg: Int)
}
-~~~
+```
The closest one can get to a default implementation is to provide a top-level generic function that takes explicit `self` as the first argument:
-~~~{swift}
+```swift
protocol P {
func m() /* {
f(self)
@@ -125,9 +125,9 @@ protocol P {
}
func f(_ arg: T) {
- // ...
+ <#...#>
}
-~~~
+```
> The commented-out code in the protocol helps communicate the provided functional implementation to the consumer.
@@ -139,9 +139,9 @@ The Object-Oriented paradigm is based around the concept of objects that encapsu
Take, for instance, the `contains` method:
-~~~{swift}
+```swift
func contains(seq: S, x: S.Generator.Element) -> Bool
-~~~
+```
Because of the constraint on the element of the sequence generator being `Equatable`, this cannot be declared on a generic container, without thereby requiring elements in that collection to conform to `Equatable`.
diff --git a/2014-09-08-optionset.md b/2014-09-08-optionset.md
new file mode 100644
index 00000000..28cb0c2a
--- /dev/null
+++ b/2014-09-08-optionset.md
@@ -0,0 +1,296 @@
+---
+title: OptionSet
+author: Mattt
+category: Swift
+excerpt: >-
+ Objective-C uses the `NS_OPTIONS` macro
+ to define set of values that may be combined together.
+ Swift imports those types as structures
+ conforming to the `OptionSet` protocol.
+ But could new language features in Swift provide a better option?
+revisions:
+ "2014-09-09": Original publication
+ "2018-11-07": Updated for Swift 4.2
+status:
+ swift: 4.2
+ reviewed: November 7, 2018
+---
+
+Objective-C uses the
+[`NS_OPTIONS`](https://nshipster.com/ns_enum-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:
+
+```objc
+typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
+ UIViewAutoresizingNone = 0,
+ UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
+ UIViewAutoresizingFlexibleWidth = 1 << 1,
+ UIViewAutoresizingFlexibleRightMargin = 1 << 2,
+ UIViewAutoresizingFlexibleTopMargin = 1 << 3,
+ UIViewAutoresizingFlexibleHeight = 1 << 4,
+ UIViewAutoresizingFlexibleBottomMargin = 1 << 5
+};
+```
+
+Swift imports this and other types defined using the `NS_OPTIONS` macro
+as a structure that conforms to the `OptionSet` protocol.
+
+```swift
+extension UIView {
+ struct AutoresizingMask: OptionSet {
+ init(rawValue: UInt)
+
+ static var flexibleLeftMargin: UIView.AutoresizingMask
+ static var flexibleWidth: UIView.AutoresizingMask
+ static var flexibleRightMargin: UIView.AutoresizingMask
+ static var flexibleTopMargin: UIView.AutoresizingMask
+ static var flexibleHeight: UIView.AutoresizingMask
+ static var flexibleBottomMargin: UIView.AutoresizingMask
+ }
+}
+```
+
+{% info %}
+The renaming and nesting of imported types
+are the result of a separate mechanism.
+{% endinfo %}
+
+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](https://developer.apple.com/documentation/swift/optionset),
+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):
+
+```swift
+view.autoresizingMask = .flexibleHeight
+```
+
+`OptionSet` conforms to the
+[`SetAlgebra`](https://developer.apple.com/documentation/swift/setalgebra)
+protocol,
+so to you can specify multiple options with an array literal ---
+no bitwise operations required:
+
+```swift
+view.autoresizingMask = [.flexibleHeight, .flexibleWidth]
+```
+
+To specify no options,
+pass an empty array literal (`[]`):
+
+```swift
+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:
+
+```swift
+struct Toppings: OptionSet {
+ let rawValue: Int
+
+ static let pepperoni = Toppings(rawValue: 1 << 0)
+ static let onions = Toppings(rawValue: 1 << 1)
+ static let bacon = Toppings(rawValue: 1 << 2)
+ static let extraCheese = Toppings(rawValue: 1 << 3)
+ static let greenPeppers = Toppings(rawValue: 1 << 4)
+ static let pineapple = Toppings(rawValue: 1 << 5)
+
+ static let meatLovers: Toppings = [.pepperoni, .bacon]
+ static let hawaiian: Toppings = [.pineapple, .bacon]
+ static let all: Toppings = [
+ .pepperoni, .onions, .bacon,
+ .extraCheese, .greenPeppers, .pineapple
+ ]
+}
+```
+
+Taken into a larger example for context:
+
+```swift
+struct Pizza {
+ enum Style {
+ case neapolitan, sicilian, newHaven, deepDish
+ }
+
+ struct Toppings: OptionSet { ... }
+
+ let diameter: Int
+ let style: Style
+ let toppings: Toppings
+
+ init(inchesInDiameter diameter: Int,
+ style: Style,
+ toppings: Toppings = [])
+ {
+ self.diameter = diameter
+ self.style = style
+ self.toppings = toppings
+ }
+}
+
+let dinner = Pizza(inchesInDiameter: 12,
+ style: .neapolitan,
+ toppings: [.greenPeppers, .pineapple])
+```
+
+Another advantage of `OptionSet` conforming to `SetAlgebra` is that
+you can perform set operations like determining membership,
+inserting and removing elements,
+and forming unions and intersections.
+This makes it easy to, for example,
+determine whether the pizza toppings are vegetarian-friendly:
+
+```swift
+extension Pizza {
+ var isVegetarian: Bool {
+ return toppings.isDisjoint(with: [.pepperoni, .bacon])
+ }
+}
+
+dinner.isVegetarian // true
+```
+
+## A Fresh Take on an Old Classic
+
+Alright, now that you know how to use `OptionSet`,
+let's show you how not to use `OptionSet`.
+
+As we mentioned before,
+new language features in Swift 4.2 make it possible
+to have our cake pizza pie and eat it too.
+
+First, declare a new `Option` protocol
+that inherits `RawRepresentable`, `Hashable`, and `CaseIterable`.
+
+```swift
+protocol Option: RawRepresentable, Hashable, CaseIterable {}
+```
+
+Next, declare an enumeration with `String` raw values
+that adopts the `Option` protocol:
+
+```swift
+enum Topping: String, Option {
+ case pepperoni, onions, bacon,
+ extraCheese, greenPeppers, pineapple
+}
+```
+
+Compare the structure declaration from before
+to the following enumeration.
+Much nicer, right?
+Just wait --- it gets even better.
+
+Automatic synthesis of `Hashable` provides effortless usage with `Set`,
+which gets us halfway to the functionality of `OptionSet`.
+Using conditional conformance,
+we can create an extension for any `Set` whose element is a `Topping`
+and define our named topping combos.
+As a bonus, `CaseIterable` makes it easy to order a pizza with _"the works"_:
+
+```swift
+extension Set where Element == Topping {
+ static var meatLovers: Set {
+ return [.pepperoni, .bacon]
+ }
+
+ static var hawaiian: Set {
+ return [.pineapple, .bacon]
+ }
+
+ static var all: Set {
+ return Set(Element.allCases)
+ }
+}
+
+typealias Toppings = Set
+```
+
+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:
+
+```swift
+extension Set where Element: Option {
+ var rawValue: Int {
+ var rawValue = 0
+ for (index, element) in Element.allCases.enumerated() {
+ if self.contains(element) {
+ rawValue |= (1 << index)
+ }
+ }
+
+ return rawValue
+ }
+}
+```
+
+Because `OptionSet` and `Set` both conform to `SetAlgebra`
+our new `Topping` implementation can be swapped in for the original one
+without needing to change anything about the `Pizza` itself.
+
+{% warning %}
+This approach assumes that the order of cases provided by `CaseIterable`
+is stable across launches.
+If it isn't, the generated raw value for combinations of options
+may be inconsistent.
+{% endwarning %}
+
+---
+
+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.
diff --git a/2014-09-08-rawoptionsettype.md b/2014-09-08-rawoptionsettype.md
deleted file mode 100644
index 922c239d..00000000
--- a/2014-09-08-rawoptionsettype.md
+++ /dev/null
@@ -1,184 +0,0 @@
----
-title: RawOptionSetType
-author: Mattt Thompson
-category: Swift
-tags: swift
-excerpt: "Swift enumerations are a marked improvement over the `NS_ENUM` macro in Objective-C. Unfortunately, `NS_OPTIONS` does not compare as favorably."
-status:
- swift: 1.2
----
-
-In Objective-C, [`NS_ENUM` & `NS_OPTIONS`](http://nshipster.com/ns_enum-ns_options/) are used to annotate C `enum`s in such a way that sets clear expectations for both the compiler and developer. Since being introduced to Objective-C with Xcode 4.5, these macros have become a standard convention in system frameworks, and a best practice within the community.
-
-In Swift, enumerations are codified as a first-class language construct as fundamental as a `struct` or `class`, and include a number of features that make them even more expressive, like raw types and associated values. They're so perfectly-suited to encapsulating closed sets of fixed values, that developers would do well to actively seek out opportunities to use them.
-
-When interacting with frameworks like Foundation in Swift, all of those `NS_ENUM` declarations are automatically converted into an `enum`—often improving on the original Objective-C declaration by eliminating naming redundancies:
-
-~~~{swift}
-enum UITableViewCellStyle : Int {
- case Default
- case Value1
- case Value2
- case Subtitle
-}
-~~~
-
-~~~{objective-c}
-typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
- UITableViewCellStyleDefault,
- UITableViewCellStyleValue1,
- UITableViewCellStyleValue2,
- UITableViewCellStyleSubtitle
-};
-~~~
-
-Unfortunately, for `NS_OPTIONS`, the Swift equivalent is arguably worse:
-
-~~~{swift}
-struct UIViewAutoresizing : RawOptionSetType {
- init(_ value: UInt)
- var value: UInt
- static var None: UIViewAutoresizing { get }
- static var FlexibleLeftMargin: UIViewAutoresizing { get }
- static var FlexibleWidth: UIViewAutoresizing { get }
- static var FlexibleRightMargin: UIViewAutoresizing { get }
- static var FlexibleTopMargin: UIViewAutoresizing { get }
- static var FlexibleHeight: UIViewAutoresizing { get }
- static var FlexibleBottomMargin: UIViewAutoresizing { get }
-}
-~~~
-
-~~~{objective-c}
-typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
- UIViewAutoresizingNone = 0,
- UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
- UIViewAutoresizingFlexibleWidth = 1 << 1,
- UIViewAutoresizingFlexibleRightMargin = 1 << 2,
- UIViewAutoresizingFlexibleTopMargin = 1 << 3,
- UIViewAutoresizingFlexibleHeight = 1 << 4,
- UIViewAutoresizingFlexibleBottomMargin = 1 << 5
-};
-~~~
-
-* * *
-
-`RawOptionsSetType` is the Swift equivalent of `NS_OPTIONS` (or at least as close as it gets). It is a protocol that adopts the `RawRepresentable`, `Equatable`, `BitwiseOperationsType`, and `NilLiteralConvertible` protocols. An option type can be represented by a `struct` conforming to `RawOptionsSetType`.
-
-Why does this suck so much? Well, the same integer bitmasking tricks in C don't work for enumerated types in Swift. An `enum` represents a type with a closed set of valid options, without a built-in mechanism for representing a conjunction of options for that type. An `enum` could, ostensibly, define a case for all possible combinations of values, but for `n > 3`, the combinatorics make this approach untenable. There are a few different ways `NS_OPTIONS` could be implemented in Swift, but `RawOptionSetType` is probably the least bad.
-
-Compared to the syntactically concise `enum` declaration, `RawOptionsSetType` is awkward and cumbersome, requiring over a dozen lines of boilerplate for computed properties:
-
-~~~{swift}
-struct Toppings : RawOptionSetType, BooleanType {
- private var value: UInt = 0
-
- init(_ value: UInt) {
- self.value = value
- }
-
- // MARK: RawOptionSetType
-
- static func fromMask(raw: UInt) -> Toppings {
- return self(raw)
- }
-
- // MARK: RawRepresentable
-
- static func fromRaw(raw: UInt) -> Toppings? {
- return self(raw)
- }
-
- func toRaw() -> UInt {
- return value
- }
-
- // MARK: BooleanType
-
- var boolValue: Bool {
- return value != 0
- }
-
-
- // MARK: BitwiseOperationsType
-
- static var allZeros: Toppings {
- return self(0)
- }
-
- // MARK: NilLiteralConvertible
-
- static func convertFromNilLiteral() -> Toppings {
- return self(0)
- }
-
- // MARK: -
-
- static var None: Toppings { return self(0b0000) }
- static var ExtraCheese: Toppings { return self(0b0001) }
- static var Pepperoni: Toppings { return self(0b0010) }
- static var GreenPepper: Toppings { return self(0b0100) }
- static var Pineapple: Toppings { return self(0b1000) }
-}
-~~~
-
-> As of Xcode 6 Beta 6, `RawOptionSetType` no longer conforms to `BooleanType`, which is required for performing bitwise checks.
-
-One nice thing about doing this in Swift is its built-in binary integer literal notation, which allows the bitmask to be computed visually. And once the options type is declared, the usage syntax is not too bad.
-
-Taken into a larger example for context:
-
-~~~{swift}
-struct Pizza {
- enum Style {
- case Neopolitan, Sicilian, NewHaven, DeepDish
- }
-
- struct Toppings : RawOptionSetType { ... }
-
- let diameter: Int
- let style: Style
- let toppings: Toppings
-
- init(inchesInDiameter diameter: Int, style: Style, toppings: Toppings = .None) {
- self.diameter = diameter
- self.style = style
- self.toppings = toppings
- }
-}
-
-let dinner = Pizza(inchesInDiameter: 12, style: .Neopolitan, toppings: .Pepperoni | .GreenPepper)
-~~~
-
-A value membership check can be performed with the `&` operator, just like with unsigned integers in C:
-
-~~~{swift}
-extension Pizza {
- var isVegetarian: Bool {
- return toppings & Toppings.Pepperoni ? false : true
- }
-}
-
-dinner.isVegetarian // false
-~~~
-
-* * *
-
-In all fairness, it may be too early to really appreciate what role option types will have in the new language. It could very well be that Swift's other constructs, like tuples or pattern matching—or indeed, even `enum`s—make options little more than a vestige of the past.
-
-Either way, if you're looking to implement an `NS_OPTIONS` equivalent in your code base, here's an [Xcode snippet](http://nshipster.com/xcode-snippets/)-friendly example of how to go about it:
-
-~~~{swift}
-struct <# Options #> : RawOptionSetType, BooleanType {
- let rawValue: UInt
- init(nilLiteral: ()) { self.value = 0 }
- init(_ value: UInt = 0) { self.value = value }
- init(rawValue value: UInt) { self.value = value }
- var boolValue: Bool { return value != 0 }
- var rawValue: UInt { return value }
- static var allZeros: <# Options #> { return self(0) }
-
- static var None: <# Options #> { return self(0b0000) }
- static var <# Option #>: <# Options #> { return self(0b0001) }
- // ...
-}
-~~~
diff --git a/2014-09-15-image-resizing.md b/2014-09-15-image-resizing.md
deleted file mode 100644
index e43a170a..00000000
--- a/2014-09-15-image-resizing.md
+++ /dev/null
@@ -1,246 +0,0 @@
----
-title: Image Resizing Techniques
-author: Mattt Thompson
-category: ""
-excerpt: "Since time immemorial, iOS developers have been perplexed by a singular question: 'How do you resize an image?'. This article endeavors to provide a clear answer to this eternal question."
-status:
- swift: 2.0
- reviewed: September 30, 2015
-revisions:
- "2014-09-15": Original publication.
- "2015-09-30": Revised for Swift 2.0, `vImage` method added.
----
-
-Since time immemorial, iOS developers have been perplexed by a singular question: "How do you resize an image?". It is a question of beguiling clarity, spurred on by a mutual mistrust of developer and platform. A thousand code samples litter web search results, each claiming to be the One True Solution, and all the others false prophets.
-
-It's embarrassing, really.
-
-This week's article endeavors to provide a clear explanation of the various approaches to image resizing on iOS (and OS X, making the appropriate `UIImage` → `NSImage` conversions), using empirical evidence to offer insights into the performance characteristics of each approach, rather than simply prescribing any one way for all situations.
-
-**Before reading any further, please note the following:**
-
-When setting a `UIImage` on a `UIImageView`, manual resizing is unnecessary for the vast majority of use cases. Instead, one can simply set the `contentMode` property to either `.ScaleAspectFit` to ensure that the entire image is visible within the frame of the image view, or `.ScaleAspectFill` to have the entire image view filled by the image, cropping as necessary from the center.
-
-```swift
-imageView.contentMode = .ScaleAspectFit
-imageView.image = image
-```
-
-* * *
-
-## Determining Scaled Size
-
-Before doing any image resizing, one must first determine the target size to scale to.
-
-### Scaling by Factor
-
-The simplest way to scale an image is by a constant factor. Generally, this involves dividing by a whole number to reduce the original size (rather than multiplying by a whole number to magnify).
-
-A new `CGSize` can be computed by scaling the width and height components individually:
-
-```swift
-let size = CGSize(width: image.size.width / 2, height: image.size.height / 2)
-```
-
-...or by applying a `CGAffineTransform`:
-
-```swift
-let size = CGSizeApplyAffineTransform(image.size, CGAffineTransformMakeScale(0.5, 0.5))
-```
-
-### Scaling by Aspect Ratio
-
-It's often useful to scale the original size in such a way that fits within a rectangle without changing the original aspect ratio. `AVMakeRectWithAspectRatioInsideRect` is a useful function found in the AVFoundation framework that takes care of that calculation for you:
-
-```swift
-import AVFoundation
-let rect = AVMakeRectWithAspectRatioInsideRect(image.size, imageView.bounds)
-```
-
-## Resizing Images
-
-There are a number of different approaches to resizing an image, each with different capabilities and performance characteristics.
-
-### `UIGraphicsBeginImageContextWithOptions` & `UIImage -drawInRect:`
-
-The highest-level APIs for image resizing can be found in the UIKit framework. Given a `UIImage`, a temporary graphics context can be used to render a scaled version, using `UIGraphicsBeginImageContextWithOptions()` and `UIGraphicsGetImageFromCurrentImageContext()`:
-
-```swift
-let image = UIImage(contentsOfFile: self.URL.absoluteString!)
-
-let size = CGSizeApplyAffineTransform(image.size, CGAffineTransformMakeScale(0.5, 0.5))
-let hasAlpha = false
-let scale: CGFloat = 0.0 // Automatically use scale factor of main screen
-
-UIGraphicsBeginImageContextWithOptions(size, !hasAlpha, scale)
-image.drawInRect(CGRect(origin: CGPointZero, size: size))
-
-let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
-UIGraphicsEndImageContext()
-```
-
-`UIGraphicsBeginImageContextWithOptions()` creates a temporary rendering context into which the original is drawn. The first argument, `size`, is the target size of the scaled image. The second argument, `isOpaque` is used to determine whether an alpha channel is rendered. Setting this to `false` for images without transparency (i.e. an alpha channel) may result in an image with a pink hue. The third argument `scale` is the display scale factor. When set to `0.0`, the scale factor of the main screen is used, which for Retina displays is `2.0` or higher (`3.0` on the iPhone 6 Plus).
-
-### `CGBitmapContextCreate` & `CGContextDrawImage`
-
-Core Graphics / Quartz 2D offers a lower-level set of APIs that allow for more advanced configuration. Given a `CGImage`, a temporary bitmap context is used to render the scaled image, using `CGBitmapContextCreate()` and `CGBitmapContextCreateImage()`:
-
-```swift
-let cgImage = UIImage(contentsOfFile: self.URL.absoluteString!).CGImage
-
-let width = CGImageGetWidth(cgImage) / 2
-let height = CGImageGetHeight(cgImage) / 2
-let bitsPerComponent = CGImageGetBitsPerComponent(cgImage)
-let bytesPerRow = CGImageGetBytesPerRow(cgImage)
-let colorSpace = CGImageGetColorSpace(cgImage)
-let bitmapInfo = CGImageGetBitmapInfo(cgImage)
-
-let context = CGBitmapContextCreate(nil, width, height, bitsPerComponent, bytesPerRow, colorSpace, bitmapInfo.rawValue)
-
-CGContextSetInterpolationQuality(context, kCGInterpolationHigh)
-
-CGContextDrawImage(context, CGRect(origin: CGPointZero, size: CGSize(width: CGFloat(width), height: CGFloat(height))), cgImage)
-
-let scaledImage = CGBitmapContextCreateImage(context).flatMap { UIImage(CGImage: $0) }
-```
-
-`CGBitmapContextCreate` takes several arguments to construct a context with desired dimensions and amount of memory for each channel within a given colorspace. In the example, these values are fetched from the `CGImage`. Next, `CGContextSetInterpolationQuality` allows for the context to interpolate pixels at various levels of fidelity. In this case, `kCGInterpolationHigh` is passed for best results. `CGContextDrawImage` allows for the image to be drawn at a given size and position, allowing for the image to be cropped on a particular edge or to fit a set of image features, such as faces. Finally, `CGBitmapContextCreateImage` creates a `CGImage` from the context.
-
-### `CGImageSourceCreateThumbnailAtIndex`
-
-Image I/O is a powerful, yet lesser-known framework for working with images. Independent of Core Graphics, it can read and write between many different formats, access photo metadata, and perform common image processing operations. The framework offers the fastest image encoders and decoders on the platform, with advanced caching mechanisms and even the ability to load images incrementally.
-
-`CGImageSourceCreateThumbnailAtIndex` offers a concise API with different options than found in equivalent Core Graphics calls:
-
-```swift
-import ImageIO
-
-if let imageSource = CGImageSourceCreateWithURL(self.URL, nil) {
- let options: [NSString: NSObject] = [
- kCGImageSourceThumbnailMaxPixelSize: max(size.width, size.height) / 2.0,
- kCGImageSourceCreateThumbnailFromImageAlways: true
- ]
-
- let scaledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options).flatMap { UIImage(CGImage: $0) }
-}
-```
-
-Given a `CGImageSource` and set of options, `CGImageSourceCreateThumbnailAtIndex` creates a thumbnail image. Resizing is accomplished by the `kCGImageSourceThumbnailMaxPixelSize`. Specifying the maximum dimension divided by a constant factor scales the image while maintaining the original aspect ratio. By specifying either `kCGImageSourceCreateThumbnailFromImageIfAbsent` or `kCGImageSourceCreateThumbnailFromImageAlways`, Image I/O will automatically cache the scaled result for subsequent calls.
-
-### Lanczos Resampling with Core Image
-
-Core Image provides a built-in [Lanczos resampling](http://en.wikipedia.org/wiki/Lanczos_resampling) functionality with the `CILanczosScaleTransform` filter. Although arguably a higher-level API than UIKit, the pervasive use of key-value coding in Core Image makes it unwieldy.
-
-That said, at least the pattern is consistent. The process of creating a transform filter, configuring it, and rendering an output image is just like any other Core Image workflow:
-
-```swift
-let image = CIImage(contentsOfURL: self.URL)
-
-let filter = CIFilter(name: "CILanczosScaleTransform")!
-filter.setValue(image, forKey: "inputImage")
-filter.setValue(0.5, forKey: "inputScale")
-filter.setValue(1.0, forKey: "inputAspectRatio")
-let outputImage = filter.valueForKey("outputImage") as! CIImage
-
-let context = CIContext(options: [kCIContextUseSoftwareRenderer: false])
-let scaledImage = UIImage(CGImage: self.context.createCGImage(outputImage, fromRect: outputImage.extent()))
-```
-
-`CILanczosScaleTransform` accepts an `inputImage`, `inputScale`, and `inputAspectRatio`, all of which are pretty self-explanatory. A `CIContext` is used to create a `UIImage` by way of a `CGImageRef` intermediary representation, since `UIImage(CIImage:)` doesn't often work as expected.
-
-Creating a `CIContext` is an expensive operation, so a cached context should always be used for repeated resizing. A `CIContext` can be created using either the GPU or the CPU (much slower) for rendering—use the `kCIContextUseSoftwareRenderer` key in the options dictionary to specify which.
-
-
-### `vImage` in Accelerate
-
-The [Accelerate framework](https://developer.apple.com/library/prerelease/ios/documentation/Accelerate/Reference/AccelerateFWRef/index.html#//apple_ref/doc/uid/TP40009465) includes a suite of `vImage` image-processing functions, with a [set of functions](https://developer.apple.com/library/prerelease/ios/documentation/Performance/Reference/vImage_geometric/index.html#//apple_ref/doc/uid/TP40005490-CH212-145717) that scale an image buffer. These lower-level APIs promise high performance with low power consumption, but at the cost of managing the buffers yourself. The following is a Swift version of a method [suggested by Nyx0uf on GitHub](https://gist.github.com/Nyx0uf/217d97f81f4889f4445a):
-
-```swift
-let cgImage = UIImage(contentsOfFile: self.URL.absoluteString!).CGImage
-
-// create a source buffer
-var format = vImage_CGImageFormat(bitsPerComponent: 8, bitsPerPixel: 32, colorSpace: nil,
- bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.First.rawValue),
- version: 0, decode: nil, renderingIntent: CGColorRenderingIntent.RenderingIntentDefault)
-var sourceBuffer = vImage_Buffer()
-defer {
- sourceBuffer.data.dealloc(Int(sourceBuffer.height) * Int(sourceBuffer.height) * 4)
-}
-
-var error = vImageBuffer_InitWithCGImage(&sourceBuffer, &format, nil, cgImage, numericCast(kvImageNoFlags))
-guard error == kvImageNoError else { return nil }
-
-// create a destination buffer
-let scale = UIScreen.mainScreen().scale
-let destWidth = Int(image.size.width * 0.5 * scale)
-let destHeight = Int(image.size.height * 0.5 * scale)
-let bytesPerPixel = CGImageGetBitsPerPixel(image.CGImage) / 8
-let destBytesPerRow = destWidth * bytesPerPixel
-let destData = UnsafeMutablePointer.alloc(destHeight * destBytesPerRow)
-defer {
- destData.dealloc(destHeight * destBytesPerRow)
-}
-var destBuffer = vImage_Buffer(data: destData, height: vImagePixelCount(destHeight), width: vImagePixelCount(destWidth), rowBytes: destBytesPerRow)
-
-// scale the image
-error = vImageScale_ARGB8888(&sourceBuffer, &destBuffer, nil, numericCast(kvImageHighQualityResampling))
-guard error == kvImageNoError else { return nil }
-
-// create a CGImage from vImage_Buffer
-let destCGImage = vImageCreateCGImageFromBuffer(&destBuffer, &format, nil, nil, numericCast(kvImageNoFlags), &error)?.takeRetainedValue()
-guard error == kvImageNoError else { return nil }
-
-// create a UIImage
-let scaledImage = destCGImage.flatMap { UIImage(CGImage: $0, scale: 0.0, orientation: image.imageOrientation) }
-```
-
-The Accelerate APIs used here clearly operate at a lower-level than the other resizing methods. To use this method, you first create a source buffer from your CGImage using a `vImage_CGImageFormat` with `vImageBuffer_InitWithCGImage()`. The destination buffer is allocated at the desired image resolution, then `vImageScale_ARGB8888` does the actual work of resizing the image. Managing your own buffers when operating on images larger than your app's memory limit is left as an exercise for the reader.
-
-
----
-
-## Performance Benchmarks
-
-So how do these various approaches stack up to one another?
-
-Here are the results of a set of [performance benchmarks](http://nshipster.com/benchmarking/) done on an iPhone 6 running iOS 8.4, via [this project](https://github.com/natecook1000/Image-Resizing):
-
-### JPEG
-
-Loading, scaling, and displaying a large, high-resolution (12000 ⨉ 12000 px 20 MB JPEG) source image from [NASA Visible Earth](http://visibleearth.nasa.gov/view.php?id=78314) at 1/10th the size:
-
-| Operation | Time _(sec)_ | σ |
-|------------------------------------|--------------|------|
-| `UIKit` | 0.612 | 14% |
-| `Core Graphics` 1 | 0.266 | 3% |
-| `Image I/O` | 0.255 | 2% |
-| `Core Image` 2 | 3.703 | 33% |
-| `vImage` 3 | -- | -- |
-
-### PNG
-
-Loading, scaling, and displaying a reasonably large (1024 ⨉ 1024 px 1MB PNG) rendering of the [Postgres.app](http://postgresapp.com) Icon at 1/10th the size:
-
-| Operation | Time _(sec)_ | σ |
-|------------------------------------|--------------|------|
-| `UIKit` | 0.044 | 30% |
-| `Core Graphics` 4 | 0.036 | 10% |
-| `Image I/O` | 0.038 | 11% |
-| `Core Image` 5 | 0.053 | 68% |
-| `vImage` | 0.050 | 25% |
-
-1, 4 Results were consistent across different values of `CGInterpolationQuality`, with negligible differences in performance benchmarks.
-
-3 The size of the NASA Visible Earth image was larger than could be processed in a single pass on the device.
-
-2, 5 Setting `kCIContextUseSoftwareRenderer` to `true` on the options passed on `CIContext` creation yielded results an order of magnitude slower than base results.
-
-## Conclusions
-
-- **UIKit**, **Core Graphics**, and **Image I/O** all perform well for scaling operations on most images.
-- **Core Image** is outperformed for image scaling operations. In fact, it is specifically recommended in the [Performance Best Practices section of the Core Image Programming Guide](https://developer.apple.com/library/mac/documentation/graphicsimaging/Conceptual/CoreImaging/ci_performance/ci_performance.html#//apple_ref/doc/uid/TP30001185-CH10-SW1) to use Core Graphics or Image I/O functions to crop or downsample images beforehand.
-- For general image scaling without any additional functionality, **`UIGraphicsBeginImageContextWithOptions`** is probably the best option.
-- If image quality is a consideration, consider using **`CGBitmapContextCreate`** in combination with **`CGContextSetInterpolationQuality`**.
-- When scaling images with the intent purpose of displaying thumbnails, **`CGImageSourceCreateThumbnailAtIndex`** offers a compelling solution for both rendering and caching.
-- Unless you're already working with **`vImage`**, the extra work to use the low-level Accelerate framework for resizing doesn't pay off.
-
diff --git a/2014-09-16-phimagemanager.md b/2014-09-16-phimagemanager.md
index 45fa2502..2d2c6934 100644
--- a/2014-09-16-phimagemanager.md
+++ b/2014-09-16-phimagemanager.md
@@ -1,18 +1,19 @@
---
title: PHImageManager
-author: Mattt Thompson
+author: Mattt
category: Cocoa
excerpt: "Yesterday's article described various techniques for resizing images using APIs from the UIKit, Core Graphics, Core Image, and Image I/O frameworks. However, that article failed to mention some rather extraordinary functionality baked into the new Photos framework which takes care of all of this for you."
+retired: true
status:
- swift: 2.0
- reviewed: September 15, 2015
+ swift: 2.0
+ reviewed: September 15, 2015
---
-[Yesterday's article](http://nshipster.com/image-resizing/) described various techniques for resizing images using APIs from the UIKit, Core Graphics, Core Image, and Image I/O frameworks. However, that article failed to mention some rather extraordinary functionality baked into the new Photos framework which takes care of all of this for you.
+[Yesterday's article](https://nshipster.com/image-resizing/) described various techniques for resizing images using APIs from the UIKit, Core Graphics, Core Image, and Image I/O frameworks. However, that article failed to mention some rather extraordinary functionality baked into the new Photos framework which takes care of all of this for you.
For anyone developing apps that manage photos or videos, meet your new best friend: `PHImageManager`.
-* * *
+---
New in iOS 8, the Photos framework is something of a triumph for the platform. Photography is one of the key verticals for the iPhone: in addition to being the [most popular cameras in the world](https://www.flickr.com/cameras), photo & video apps are regularly featured on the App Store. This framework goes a long way to empower apps to do even more, with a shared set of tools and primitives.
@@ -24,7 +25,7 @@ A great example of this is `PHImageManager`, which acts as a centralized coordin
But first, here's a simple example of how a table view might asynchronously load cell images with asset thumbnails:
-~~~{swift}
+```swift
import Photos
var assets: [PHAsset]
@@ -58,9 +59,9 @@ func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexP
return cell
}
-~~~
+```
-~~~{objective-c}
+```objc
@import Photos;
@property (nonatomic, strong) NSArray *assets;
@@ -95,7 +96,7 @@ func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexP
return cell;
}
-~~~
+```
API usage is pretty straightforward: the `defaultManager` asynchronously requests an image for the asset corresponding to the cell at a particular index path, and the cell image view is set whenever the result comes back. The only tricky part is handling cell reuse—(1) before assigning the resulting image to the cell's image view, we call `cellForRowAtIndexPath` to be sure we're working with the right cell, and (2) we use the cell's `tag` to keep track of image requests, in order to cancel any pending requests when a cell is reused.
@@ -105,7 +106,7 @@ If there's reasonable assurance that most of a set of assets will be viewed at s
For example, here's how the results of a `PHAsset` fetch operation can be pre-cached in order to optimize image availability:
-~~~{swift}
+```swift
let cachingImageManager = PHCachingImageManager()
let options = PHFetchOptions()
@@ -127,9 +128,9 @@ cachingImageManager.startCachingImagesForAssets(assets,
contentMode: .AspectFit,
options: nil
)
-~~~
+```
-~~~{objective-c}
+```objc
PHCachingImageManager *cachingImageManager = [[PHCachingImageManager alloc] init];
PHFetchOptions *options = [[PHFetchOptions alloc] init];
@@ -150,11 +151,11 @@ NSMutableArray *assets = [[NSMutableArray alloc] init];
targetSize:PHImageManagerMaximumSize
contentMode:PHImageContentModeAspectFit
options:nil];
-~~~
+```
Alternatively, Swift `willSet` / `didSet` hooks offer a convenient way to automatically start pre-caching assets as they are loaded:
-~~~{swift}
+```swift
let cachingImageManager = PHCachingImageManager()
var assets: [PHAsset] = [] {
willSet {
@@ -169,7 +170,7 @@ var assets: [PHAsset] = [] {
)
}
}
-~~~
+```
## PHImageRequestOptions
@@ -197,7 +198,7 @@ Several of these properties take a specific `enum` type, which are all pretty se
Using `PHImageManager` and `PHImageRequestOptions` to their full capacity allows for rather sophisticated functionality. One could, for example, use successive image requests to crop full-quality assets to any faces detected in the image.
-~~~{swift}
+```swift
let asset: PHAsset
@IBOutlet weak var imageView: UIImageView!
@@ -251,9 +252,9 @@ override func viewDidLoad() {
}
}
}
-~~~
+```
-~~~{objective-c}
+```objc
@property (nonatomic, strong) PHAsset *asset;
@property (nonatomic, weak) IBOutlet UIImageView *imageView;
@property (nonatomic, weak) IBOutlet UIProgressView *progressView;
@@ -309,13 +310,13 @@ override func viewDidLoad() {
resultHandler:resultHandler];
}
-~~~
+```
The initial request attempts to get the most readily available representation of an asset to pass into a `CIDetector` for facial recognition. If any features were detected, the final request would be cropped to them, by specifying the `normalizedCropRect` on the final `PHImageRequestOptions`.
> `normalizedCropRect` is normalized for `origin` and `size` components within the inclusive range `0.0` to `1.0`. An affine transformation scaling on the inverse of the original frame makes for an easy calculation.
-* * *
+---
From its very inception, iOS has been a balancing act between functionality and integrity. And with every release, a combination of thoughtful affordances and powerful APIs have managed to expand the role third-party applications without compromising security or performance.
diff --git a/2014-09-22-equatable-and-comparable.md b/2014-09-22-equatable-and-comparable.md
new file mode 100644
index 00000000..133917ba
--- /dev/null
+++ b/2014-09-22-equatable-and-comparable.md
@@ -0,0 +1,352 @@
+---
+title: Equatable and Comparable
+author: Mattt
+category: Swift
+tags: swift
+excerpt: >-
+ Objective-C required us to wax philosophic
+ about the nature of equality and identity.
+ To the relief of any developer less inclined towards discursive treatises,
+ this is not as much the case for Swift.
+revisions:
+ "2014-09-22": Original publication
+ "2018-12-19": Updated for Swift 4.2
+status:
+ swift: 4.2
+ reviewed: December 19, 2018
+---
+
+Objective-C required us to
+[wax philosophic](/equality/)
+about the nature of equality and identity.
+To the relief of any developer less inclined towards discursive treatises,
+this is not as much the case for Swift.
+
+In Swift,
+there's the `Equatable` protocol,
+which explicitly defines the semantics of equality and inequality
+in a manner entirely separate from the question of identity.
+There's also the `Comparable` protocol,
+which builds on `Equatable` to refine inequality semantics
+to creating an ordering of values.
+Together, the `Equatable` and `Comparable` protocols
+form the central point of comparison throughout the language.
+
+---
+
+## Equatable
+
+Values conforming to the `Equatable` protocol
+can be evaluated for equality and inequality.
+Conformance to `Equatable` requires
+the implementation of the equality operator (`==`).
+
+As an example,
+consider the following
+[`Binomen`](https://en.wikipedia.org/wiki/Binomial_nomenclature) structure:
+
+```swift
+struct Binomen {
+ let genus: String
+ let species: String
+}
+
+let 🐺 = Binomen(genus: "Canis", species: "lupus")
+let 🐻 = Binomen(genus: "Ursus", species: "arctos")
+```
+
+We can add `Equatable` conformance through an extension,
+implementing the required type method for the `==` operator like so:
+
+```swift
+extension Binomen: Equatable {
+ static func == (lhs: Binomen, rhs: Binomen) -> Bool {
+ return lhs.genus == rhs.genus &&
+ lhs.species == rhs.species
+ }
+}
+
+🐺 == 🐺 // true
+🐺 == 🐻 // false
+```
+
+_Easy enough, right?_
+
+Well actually, it's even easier than that ---
+as of Swift 4.1,
+the compiler can _automatically synthesize_ conformance
+for structures whose stored properties all have types that are `Equatable`.
+We could replace all of the code in the extension
+by simply adopting `Equatable` in the declaration of `Binomen`:
+
+```swift
+struct Binomen: Equatable {
+ let genus: String
+ let species: String
+}
+
+🐺 == 🐺 // true
+🐺 == 🐻 // false
+```
+
+### The Benefits of Being Equal
+
+Equatability isn't just about using the `==` operator ---
+there's also the `!=` operator!
+it also lets a value,
+among other things,
+be found in a collection and
+matched in a `switch` statement.
+
+```swift
+[🐺, 🐻].contains(🐻) // true
+
+func commonName(for binomen: Binomen) -> String? {
+ switch binomen {
+ case 🐺: return "gray wolf"
+ case 🐻: return "brown bear"
+ default: return nil
+ }
+}
+commonName(for: 🐺) // "gray wolf"
+```
+
+`Equatable` is also a requirement for conformance to
+[`Hashable`](https://nshipster.com/hashable/),
+another important type in Swift.
+
+This is all to say that
+if a type has equality semantics ---
+if two values of that type can be considered equal or unequal --
+it should conform to `Equatable`.
+
+### The Limits of Automatic Synthesis
+
+The Swift standard library and most of the frameworks in Apple SDKs
+do a great job adopting `Equatable` for types that make sense to be.
+In practice, you're unlikely to be in a situation
+where the dereliction of a built-in type
+spoils automatic synthesis for your own type.
+
+Instead, the most common obstacle to automatic synthesis involves tuples.
+Consider this poorly-considered
+[`Trinomen`](https://en.wikipedia.org/wiki/Trinomen) type:
+
+```swift
+struct Trinomen {
+ let genus: String
+ let species: (String, subspecies: String?) // 🤔
+}
+
+extension Trinomen: Equatable {}
+// 🛑 Type 'Trinomen' does not conform to protocol 'Equatable'
+```
+
+As described in our article about [`Void`](/void/),
+tuples aren't nominal types,
+so they can't conform to `Equatable`.
+If you wanted to compare two trinomina for equality,
+you'd have to write the conformance code for `Equatable`.
+
+_...like some kind of animal_.
+
+### Conditional Conformance to Equality
+
+In addition to automatic synthesis of `Equatable`,
+Swift 4.1 added another critical feature:
+conditional conformance.
+
+To illustrate this,
+consider the following generic type
+that represents a quantity of something:
+
+```swift
+struct Quantity {
+ let count: Int
+ let thing: Thing
+}
+```
+
+Can `Quantity` conform to `Equatable`?
+We know that integers are equatable,
+so it really depends on what kind of `Thing` we're talking about.
+
+What conditional conformance Swift 4.1 allows us to do is
+create an extension on a type with a conditional clause.
+We can use that here to programmatically express that
+\_"a quantity of a thing is equatable if the thing itself is equatable":
+
+```swift
+extension Quantity: Equatable where Thing: Equatable {}
+```
+
+And with that declaration alone,
+Swift has everything it needs to synthesize conditional `Equatable` conformance,
+allowing us to do the following:
+
+```swift
+let oneHen = Quantity(count: 1, thing: "🐔")
+let twoDucks = Quantity(count: 2, thing: "🦆")
+oneHen == twoDucks // false
+```
+
+{% info %}
+Conditional conformance is the same mechanism that provides for
+an `Array` whose `Element` is `Equatable` to itself conform to `Equatable`:
+
+```swift
+[🐺, 🐻] == [🐺, 🐻] // true
+```
+
+{% endinfo %}
+
+### Equality by Reference
+
+For reference types,
+the notion of equality becomes conflated with identity.
+It makes sense that two `Name` structures with the same values would be equal,
+but two `Person` objects can have the same name and still be different people.
+
+For Objective-C-compatible object types,
+the `==` operator is already provided from the [`isEqual:`](/equality/) method:
+
+```swift
+import Foundation
+
+class ObjCObject: NSObject {}
+
+ObjCObject() == ObjCObject() // false
+```
+
+For Swift reference types (that is, classes),
+equality can be evaluated using the identity equality operator (`===`):
+
+```swift
+class Object: Equatable {
+ static func == (lhs: Object, rhs: Object) -> Bool {
+ return lhs === rhs
+ }
+}
+
+Object() == Object() // false
+```
+
+That said,
+`Equatable` semantics for reference types
+are often not as straightforward as a straight identity check,
+so before you add conformance to all of your classes,
+ask yourself whether it actually makes sense to do so.
+
+## Comparable
+
+Building on `Equatable`,
+the `Comparable` protocol allows for values to be considered
+less than or greater than other values.
+
+`Comparable` requires implementations for the following operators:
+
+| Operator | Name |
+| -------- | ------------------------ |
+| `<` | Less than |
+| `<=` | Less than or equal to |
+| `>=` | Greater than or equal to |
+| `>` | Greater than |
+
+...so it's surprising that you can get away with only implementing one of them:
+the `<` operator.
+
+Going back to our binomial nomenclature example,
+let's extend `Binomen` to conform to `Comparable`
+such that values are ordered alphabetically
+first by their genus name and then by their species name:
+
+```swift
+extension Binomen: Comparable {
+ static func < (lhs: Binomen, rhs: Binomen) -> Bool {
+ if lhs.genus != rhs.genus {
+ return lhs.genus < rhs.genus
+ } else {
+ return lhs.species < rhs.species
+ }
+ }
+}
+
+
+🐻 > 🐺 // true ("Ursus" lexicographically follows "Canis")
+```
+
+{% warning %}
+Implementing the `<` operator
+for types that consider more than one property
+is deceptively hard to get right the first time.
+Be sure to write test cases to validate correct behavior.
+{% endwarning %}
+
+This is _quite_ clever.
+Since the implementations of each comparison operator
+can be derived from just `<` and `==`,
+all of that functionality is made available automatically through type inference.
+
+{% info %}
+Contrast this with how Ruby and other languages derive
+equality and comparison operators from a single operator,
+`<=>` _(a.k.a the "UFO operator")_.
+A few pitches to bring formalized ordering to Swift
+have floated around over the years,
+[such as this one](https://gist.github.com/CodaFi/f0347bd37f1c407bf7ea0c429ead380e),
+but we haven't seen any real movement in this direction lately.
+{% endinfo %}
+
+### Incomparable Limitations with No Equal
+
+Unlike `Equatable`,
+the Swift compiler can't automatically synthesize conformance to `Comparable`.
+But that's not for lack of trying --- _it's just not possible_.
+
+There are no implicit semantics for comparability
+that could be derived from the types of stored properties.
+If a type has more than one stored property,
+there's no way to determine how they're compared relative to one another.
+And even if a type had only a single property whose type was `Comparable`,
+there's no guarantee how the ordering of that property
+would relate to the ordering of the value as a whole
+
+### Comparable Benefits
+
+Conforming to `Comparable` confers a multitude of benefits.
+
+One such benefit is that
+arrays containing values of comparable types
+can call methods like `sorted()`, `min()`, and `max()`:
+
+```swift
+let 🐬 = Binomen(genus: "Tursiops", species: "truncatus")
+let 🌻 = Binomen(genus: "Helianthus", species: "annuus")
+let 🍄 = Binomen(genus: "Amanita", species: "muscaria")
+let 🐶 = Binomen(genus: "Canis", species: "domesticus")
+
+let menagerie = [🐺, 🐻, 🐬, 🌻, 🍄, 🐶]
+menagerie.sorted() // [🍄, 🐶, 🐺, 🌻, 🐬, 🐻]
+menagerie.min() // 🍄
+menagerie.max() // 🐻
+```
+
+Having a defined ordering also lets you create ranges, like so:
+
+```swift
+let lessThan10 = ..<10
+lessThan10.contains(1) // true
+lessThan10.contains(11) // false
+
+let oneToFive = 1...5
+oneToFive.contains(3) // true
+oneToFive.contains(7) // false
+```
+
+---
+
+In the Swift standard library,
+`Equatable` is a type without an equal;
+`Comparable` a protocol without compare.
+Take care to adopt them in your own types as appropriate
+and you'll benefit greatly.
diff --git a/2014-09-22-swift-comparison-protocols.md b/2014-09-22-swift-comparison-protocols.md
deleted file mode 100644
index e2a05219..00000000
--- a/2014-09-22-swift-comparison-protocols.md
+++ /dev/null
@@ -1,268 +0,0 @@
----
-title: Swift Comparison Protocols
-author: Mattt Thompson
-category: Swift
-tags: swift
-excerpt: "Objective-C required us to wax philosophic about the nature of equality and identity. To the relief of any developer less inclined towards handwavy treatises, this is not as much the case for Swift."
-status:
- swift: 1.2
----
-
-Objective-C required us to [wax philosophic](http://nshipster.com/equality/) about the nature of equality and identity. To the relief of any developer less inclined towards handwavy treatises, this is not as much the case for Swift.
-
-In Swift, `Equatable` is a fundamental type, from which `Comparable` and `Hashable` are both derived. Together, these protocols form the central point of comparison throughout the language.
-
-* * *
-
-## Equatable
-
-Values of the `Equatable` type can be evaluated for equality and inequality. Declaring a type as equatable bestows several useful abilities, notably the ability values of that type to be found in a containing `Array`.
-
-For a type to be `Equatable`, there must exist an implementation of the `==` operator function, which accepts a matching type:
-
-~~~{swift}
-func ==(lhs: Self, rhs: Self) -> Bool
-~~~
-
-For value types, equality is determined by evaluating the equality of each component property. As an example, consider a `Complex` type, which takes a generic type `T`, which conforms to `SignedNumberType`:
-
-> `SignedNumberType` is a convenient choice for a generic number type, as it inherits from both `Comparable` (and thus `Equatable`, as described in the section) and `IntegerLiteralConvertible`, which `Int`, `Double`, and `Float` all conform to.
-
-~~~{swift}
-struct Complex {
- let real: T
- let imaginary: T
-}
-~~~
-
-Since a [complex number](http://en.wikipedia.org/wiki/Complex_number) is comprised of a real and imaginary component, two complex numbers are equal if and only if their respective real and imaginary components are equal:
-
-~~~{swift}
-extension Complex: Equatable {}
-
-// MARK: Equatable
-
-func ==(lhs: Complex, rhs: Complex) -> Bool {
- return lhs.real == rhs.real && lhs.imaginary == rhs.imaginary
-}
-~~~
-
-The result:
-
-~~~swift
-let a = Complex(real: 1.0, imaginary: 2.0)
-let b = Complex(real: 1.0, imaginary: 2.0)
-
-a == b // true
-a != b // false
-~~~
-
-> As described in [the article about Swift Default Protocol Implementations](http://nshipster.com/swift-default-protocol-implementations/), an implementation of `!=` is automatically derived from the provided `==` operator by the standard library.
-
-For reference types, the equality becomes conflated with identity. It makes sense that two `Name` structs with the same values would be equal, but two `Person` objects can have the same name, but be different people.
-
-For Objective-C-compatible object types, the `==` operator is already provided from the `isEqual:` method:
-
-~~~{swift}
-class ObjCObject: NSObject {}
-
-ObjCObject() == ObjCObject() // false
-~~~
-
-For Swift reference types, equality can be evaluated as an identity check on an `ObjectIdentifier` constructed with an instance of that type:
-
-~~~{swift}
-class Object: Equatable {}
-
-// MARK: Equatable
-
-func ==(lhs: Object, rhs: Object) -> Bool {
- return ObjectIdentifier(lhs) == ObjectIdentifier(rhs)
-}
-
-Object() == Object() // false
-~~~
-
-## Comparable
-
-Building on `Equatable`, the `Comparable` protocol allows for more specific inequality, distinguishing cases where the left hand value is greater than or less than the right hand value.
-
-Types conforming to the `Comparable` protocol provide the following operators:
-
-~~~{swift}
-func <=(lhs: Self, rhs: Self) -> Bool
-func >(lhs: Self, rhs: Self) -> Bool
-func >=(lhs: Self, rhs: Self) -> Bool
-~~~
-
-What's interesting about this list, however, is not so much what is _included_, but rather what's _missing_.
-
-The first and perhaps most noticeable omission is `==`, since `>=` is a logical disjunction of `>` and `==` comparisons. As a way of reconciling this, `Comparable` inherits from `Equatable`, which provides `==`.
-
-The second omission is a bit more subtle, and is actually the key to understanding what's going on here: `<`. What happened to the "less than" operator? It's defined by the `_Comparable` protocol. Why is this significant? As described in [the article about Swift Default Protocol Implementations](http://nshipster.com/swift-default-protocol-implementations/), the Swift Standard Library provides a default implementation of the `Comparable` protocol based entirely on the existential type `_Comparable`. This is actually _really_ clever. Since the implementations of all of the comparison functions can be derived from just `<` and `==`, all of that functionality is made available automatically through type inference.
-
-> Contrast this with, for example, how Ruby derives equality and comparison operators from a single operator, `<=>` (a.k.a the "UFO operator"). [Here's how this could be implemented in Swift](https://gist.github.com/mattt/7e4db72ce1b6c8a18bb4).
-
-As a more complex example, consider a `CSSSelector` struct, which implements [cascade ordering](http://www.w3.org/TR/CSS2/cascade.html#cascading-order) of selectors:
-
-~~~{swift}
-import Foundation
-
-struct CSSSelector {
- let selector: String
-
- struct Specificity {
- let id: Int
- let `class`: Int
- let element: Int
-
- init(_ components: [String]) {
- var (id, `class`, element) = (0, 0, 0)
- for token in components {
- if token.hasPrefix("#") {
- id++
- } else if token.hasPrefix(".") {
- `class`++
- } else {
- element++
- }
- }
-
- self.id = id
- self.`class` = `class`
- self.element = element
- }
- }
-
- let specificity: Specificity
-
- init(_ string: String) {
- self.selector = string
-
- // Naïve tokenization, ignoring operators, pseudo-selectors, and `style=`.
- let components: [String] = self.selector.componentsSeparatedByCharactersInSet(NSCharacterSet.whitespaceCharacterSet())
- self.specificity = Specificity(components)
- }
-}
-~~~
-
-Where as CSS selectors are evaluated by specificity rank and order, two selectors are only really equal if they resolve to the same elements:
-
-~~~{swift}
-extension CSSSelector: Equatable {}
-
-// MARK: Equatable
-
-func ==(lhs: CSSSelector, rhs: CSSSelector) -> Bool {
- // Naïve equality that uses string comparison rather than resolving equivalent selectors
- return lhs.selector == rhs.selector
-}
-~~~
-
-Instead, selectors are actually compared in terms of their specificity:
-
-~~~{swift}
-extension CSSSelector.Specificity: Comparable {}
-
-// MARK: Comparable
-
-func <(lhs: CSSSelector.Specificity, rhs: CSSSelector.Specificity) -> Bool {
- return lhs.id < rhs.id ||
- (lhs.id == rhs.id && lhs.`class` < rhs.`class`) ||
- (lhs.id == rhs.id && lhs.`class` == rhs.`class` && lhs.element < rhs.element)
-}
-
-// MARK: Equatable
-
-func ==(lhs: CSSSelector.Specificity, rhs: CSSSelector.Specificity) -> Bool {
- return lhs.id == rhs.id &&
- lhs.`class` == rhs.`class` &&
- lhs.element == rhs.element
-}
-~~~
-
-Bringing everything together:
-
-> For clarity, assume `CSSSelector` [conforms to `StringLiteralConvertible`](http://nshipster.com/swift-literal-convertible/).
-
-~~~{swift}
-let a: CSSSelector = "#logo"
-let b: CSSSelector = "html body #logo"
-let c: CSSSelector = "body div #logo"
-let d: CSSSelector = ".container #logo"
-
-b == c // false
-b.specificity == c.specificity // true
-c.specificity < a.specificity // false
-d.specificity > c.specificity // true
-~~~
-
-## Hashable
-
-Another important protocol derived from `Equatable` is `Hashable`.
-
-Only `Hashable` types can be stored as the key of a Swift `Dictionary`:
-
-~~~{swift}
-struct Dictionary : CollectionType, DictionaryLiteralConvertible { ... }
-~~~
-
-For a type to conform to `Hashable`, it must provide a getter for the `hashValue` property.
-
-~~~{swift}
-protocol Hashable : Equatable {
- /// Returns the hash value. The hash value is not guaranteed to be stable
- /// across different invocations of the same program. Do not persist the hash
- /// value across program runs.
- ///
- /// The value of `hashValue` property must be consistent with the equality
- /// comparison: if two values compare equal, they must have equal hash
- /// values.
- var hashValue: Int { get }
-}
-~~~
-
-Determining the [optimal hashing value](http://en.wikipedia.org/wiki/Perfect_hash_function) is way outside the scope of this article. Fortunately, most values can derive an adequate hash value from an `XOR` of the hash values of its component properties.
-
-The following built-in Swift types implement `hashValue`:
-
-- `Double`
-- `Float`, `Float80`
-- `Int`, `Int8`, `Int16`, `Int32`, `Int64`
-- `UInt`, `UInt8`, `UInt16`, `UInt32`, `UInt64`
-- `String`
-- `UnicodeScalar`
-- `ObjectIdentifier`
-
-Based on this, here's how a struct representing [Binomial Nomenclature in Biological Taxonomy](http://en.wikipedia.org/wiki/Binomial_nomenclature):
-
-~~~{swift}
-struct Binomen {
- let genus: String
- let species: String
-}
-
-// MARK: Hashable
-
-extension Binomen: Hashable {
- var hashValue: Int {
- return genus.hashValue ^ species.hashValue
- }
-}
-
-// MARK: Equatable
-
-func ==(lhs: Binomen, rhs: Binomen) -> Bool {
- return lhs.genus == rhs.genus && lhs.species == rhs.species
-}
-~~~
-
-Being able to hash this type makes it possible to key common name to the "Latin name":
-
-~~~{swift}
-var commonNames: [Binomen: String] = [:]
-commonNames[Binomen(genus: "Canis", species: "lupis")] = "Grey Wolf"
-commonNames[Binomen(genus: "Canis", species: "rufus")] = "Red Wolf"
-commonNames[Binomen(genus: "Canis", species: "latrans")] = "Coyote"
-commonNames[Binomen(genus: "Canis", species: "aureus")] = "Golden Jackal"
-~~~
diff --git a/2014-09-30-uialertcontroller.md b/2014-09-30-uialertcontroller.md
index 58332dc7..aeabf8aa 100644
--- a/2014-09-30-uialertcontroller.md
+++ b/2014-09-30-uialertcontroller.md
@@ -1,6 +1,6 @@
---
title: UIAlertController
-author: Mattt Thompson
+author: Mattt
category: Cocoa
excerpt: "Did you know that `UIAlertView` and `UIActionSheet` (as well as their respective delegate protocols) are deprecated in iOS 8? It's true."
status:
@@ -43,7 +43,7 @@ Rather than specifying all of an alert's buttons in an initializer, instances of
### A Standard Alert
-
+
#### The Old Way: UIAlertView
@@ -56,7 +56,7 @@ alertView.show()
func alertView(alertView: UIAlertView, clickedButtonAtIndex buttonIndex: Int) {
switch buttonIndex {
- // ...
+ <#...#>
}
}
```
@@ -67,25 +67,25 @@ func alertView(alertView: UIAlertView, clickedButtonAtIndex buttonIndex: Int) {
let alertController = UIAlertController(title: "Default Style", message: "A standard alert.", preferredStyle: .Alert)
let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel) { (action) in
- // ...
+ <#...#>
}
alertController.addAction(cancelAction)
let OKAction = UIAlertAction(title: "OK", style: .Default) { (action) in
- // ...
+ <#...#>
}
alertController.addAction(OKAction)
self.presentViewController(alertController, animated: true) {
- // ...
+ <#...#>
}
```
### A Standard Action Sheet
-
+
-#### The Old Way: UIActionSheet
+#### UIActionSheet
```swift
let actionSheet = UIActionSheet(title: "Takes the appearance of the bottom bar if specified; otherwise, same as UIActionSheetStyleDefault.", delegate: self, cancelButtonTitle: "Cancel", destructiveButtonTitle: "Destroy", otherButtonTitles: "OK")
@@ -101,18 +101,18 @@ func actionSheet(actionSheet: UIActionSheet, clickedButtonAtIndex buttonIndex: I
}
```
-#### The New Way: UIAlertController
+#### UIAlertController
```swift
let alertController = UIAlertController(title: nil, message: "Takes the appearance of the bottom bar if specified; otherwise, same as UIActionSheetStyleDefault.", preferredStyle: .ActionSheet)
let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel) { (action) in
- // ...
+ <#...#>
}
alertController.addAction(cancelAction)
let OKAction = UIAlertAction(title: "OK", style: .Default) { (action) in
- // ...
+ <#...#>
}
alertController.addAction(OKAction)
@@ -122,7 +122,7 @@ let destroyAction = UIAlertAction(title: "Destroy", style: .Destructive) { (acti
alertController.addAction(destroyAction)
self.presentViewController(alertController, animated: true) {
- // ...
+ <#...#>
}
```
@@ -132,7 +132,7 @@ self.presentViewController(alertController, animated: true) {
### Alert with Destructive Button
-
+
The type of an action is specified by `UIAlertActionStyle`, which has three values:
@@ -156,13 +156,13 @@ let destroyAction = UIAlertAction(title: "Destroy", style: .Destructive) { (acti
alertController.addAction(destroyAction)
self.presentViewController(alertController, animated: true) {
- // ...
+ <#...#>
}
```
### Alert with >2 Buttons
-
+
With one or two actions, buttons in an alert are stacked horizontally. Any more than that, though, and it takes on a display characteristic closer to an action sheet:
@@ -180,7 +180,7 @@ alertController.addAction(cancelAction)
### Creating a Login Form
-
+
iOS 5 added the `alertViewStyle` property to `UIAlertView`, which exposed much sought-after private APIs that allowed login and password fields to be displayed in an alert, as seen in several built-in system apps.
@@ -218,7 +218,7 @@ alertController.addAction(cancelAction)
### Creating a Sign Up Form
-
+
`UIAlertController` goes even further to allow any number of text fields, each with the ability to be configured and customized as necessary. This makes it possible to create a fully-functional signup form in a single modal alert:
diff --git a/2014-10-06-swift-system-version-checking.md b/2014-10-06-swift-system-version-checking.md
index 0c0a2125..ec437771 100644
--- a/2014-10-06-swift-system-version-checking.md
+++ b/2014-10-06-swift-system-version-checking.md
@@ -1,11 +1,11 @@
---
title: Swift System Version Checking
-author: Mattt Thompson
+author: Mattt
category: Swift
tags: swift
excerpt: "C uses preprocessor directives capable of unspeakable evil. Swift has a safe subset of preprocessor directives. So how do we check system version for API compatibility?"
status:
- swift: 1.0
+ swift: 4.0
---
While it's not accurate to say that Swift is "Objective-C without the C", it's for lack of resemblance to Objective-C, not the absence of C. Swift is _vehemently_ **_not_** C.
@@ -13,71 +13,72 @@ While it's not accurate to say that Swift is "Objective-C without the C", it's f
Swift certainly draws inspiration from Haskell, Rust, Python, D, and other modern languages, but one can perhaps best understand the language as a rejection of everything that's broken in C:
- C is **unsafe** by default. Swift is **safe** by default _(hence the `unsafe` naming of pointer manipulation functions)_.
-- C has **undefined behavior**. Swift has **well-defined behavior** _(or at least theoretically; the compiler tools still have some catching up to do)_.
+- C has **undefined behavior**. Swift has **well-defined behavior**.
- C uses **preprocessor directives capable of unspeakable evil**. Swift has a **safe subset of preprocessor directives**.
> One could go as far to say that Swift's type system was specifically designed out of _spite_ for C++.
In Objective-C, checking for the availability of an API was accomplished through a combination of C preprocessor directives, conditionals on `class`, `respondsToSelector:`, and `instancesRespondToSelector:`:
-~~~{objective-c}
+```objc
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000
if ([NSURLSession class] &&
[NSURLSessionConfiguration respondsToSelector:@selector(backgroundSessionConfigurationWithIdentifier:)]) {
- // ...
+ <#...#>
}
#endif
-~~~
+```
-However, as noted previously, Swift's compiler directives are [extremely constrained](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithCAPIs.html#//apple_ref/doc/uid/TP40014216-CH8-XID_20), allowing only for compiler flags and conditional compilation against specific operating systems and architectures:
+However, as noted previously, Swift's compiler directives are [extremely constrained](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithCAPIs.html#//apple_ref/doc/uid/TP40014216-CH8-XID_20), allowing only for compiler flags and conditional compilation against [specific operating systems, architectures, and language versions](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/doc/uid/TP40014097-CH33-ID539):
-~~~{swift}
+```swift
#if DEBUG
println("OTHER_SWIFT_FLAGS = -D DEBUG")
#endif
-~~~
+```
-| Function | Valid Arguments |
-|----------|------------------------------------|
-| `os()` | `OSX`, `iOS` |
-| `arch()` | `x86_64`, `arm`, `arm64`, `i386` |
+| Function | Valid Arguments |
+|-----------|--------------------------------------------|
+| `os()` | `macOS`, `iOS`, `watchOS`, `tvOS`, `Linux` |
+| `arch()` | `x86_64`, `arm`, `arm64`, `i386` |
+| `swift()` | `>=` followed by a version number |
-~~~{swift}
+```swift
#if os(iOS)
var image: UIImage?
-#elseif os(OSX)
+#elseif os(macOS)
var image: NSImage?
#endif
-~~~
+```
-Unfortunately, `os()` does not offer any insight into the specific version of OS X or iOS, which means that checks must be made at runtime. And with Swift's less-forgiving [treatment of `nil`](http://nshipster.com/nil/), checking for constants Objective-C-style results in a crash.
+Unfortunately, `os()` doesn't offer any insight into the specific version of macOS or iOS, which means that checks must be made at runtime. And with Swift's less-forgiving [treatment of `nil`](http://nshipster.com/nil/), checking for constants Objective-C-style results in a crash.
So how do you check the system version in Swift to determine API availability? Read on to find out.
* * *
-## NSProcessInfo
+## ProcessInfo
-Anticipating the need for a Swift-friendly API for determining API version at runtime, iOS 8 introduces the `operatingSystemVersion` property and `isOperatingSystemAtLeastVersion` method on `NSProcessInfo`. Both APIs use a new `NSOperatingSystemVersion` value type, which contains the `majorVersion`, `minorVersion`, and `patchVersion`.
+Anticipating the need for a Swift-friendly API for determining API version at runtime, iOS 8 introduces the `operatingSystemVersion` property and `isOperatingSystemAtLeast` method on `ProcessInfo`. Both APIs use a new `OperatingSystemVersion` value type, which contains the `majorVersion`, `minorVersion`, and `patchVersion`.
> Apple software releases follow [semantic versioning](http://semver.org) conventions.
-### isOperatingSystemAtLeastVersion
+### isOperatingSystemAtLeast
-For a simple check, like "is this app running on iOS 9?", `isOperatingSystemAtLeastVersion` is the most straightforward approach.
+For a simple check, like "is this app running on iOS 9?", `isOperatingSystemAtLeast` is the most straightforward approach.
-~~~{swift}
-if NSProcessInfo().isOperatingSystemAtLeastVersion(NSOperatingSystemVersion(majorVersion: 9, minorVersion: 0, patchVersion: 0)) {
- println("iOS >= 9.0.0")
+```swift
+if ProcessInfo().isOperatingSystemAtLeast(OperatingSystemVersion(majorVersion: 9, minorVersion: 0, patchVersion: 0)) {
+ print("iOS >= 9.0.0")
}
-~~~
+```
### operatingSystemVersion
For more involved version comparison, the `operatingSystemVersion` can be inspected directly. Combine this with Swift pattern matching and `switch` statements for syntactic concision:
-~~~{swift}
-let os = NSProcessInfo().operatingSystemVersion
+```swift
+let os = ProcessInfo().operatingSystemVersion
switch (os.majorVersion, os.minorVersion, os.patchVersion) {
case (8, 0, _):
println("iOS >= 8.0.0, < 8.1.0")
@@ -89,46 +90,44 @@ default:
// this code will have already crashed on iOS 7, so >= iOS 10.0
println("iOS >= 10.0.0")
}
-~~~
+```
## UIDevice systemVersion
-Ironically, the new `NSProcessInfo` APIs aren't especially useful at the time of writing, since they're unavailable for iOS 7.
-
As an alternative, one can use the `systemVersion` property `UIDevice`:
-~~~{swift}
-switch UIDevice.currentDevice().systemVersion.compare("8.0.0", options: NSStringCompareOptions.NumericSearch) {
-case .OrderedSame, .OrderedDescending:
- println("iOS >= 8.0")
-case .OrderedAscending:
- println("iOS < 8.0")
+```swift
+switch UIDevice.current.systemVersion.compare("8.0.0", options: .numeric) {
+case .orderedSame, .orderedDescending:
+ print("iOS >= 8")
+case .orderedAscending:
+ print("iOS < 8.0")
}
-~~~
+```
-> Use `NSStringCompareOptions.NumericSearch` when comparing version number strings to ensure that, for example, `"2.5" < "2.10"`.
+> Use `String.CompareOptions.numeric` when comparing version number strings to ensure that, for example, `"2.5" < "2.10"`.
-String comparison and `NSComparisonResult` aren't as sexy as a dedicated value type like `NSOperatingSystemVersion`, but it gets the job done all the same.
+String comparison and `ComparisonResult` aren't as sexy as a dedicated value type like `OperatingSystemVersion`, but it gets the job done all the same.
-## NSAppKitVersionNumber
+## NSAppKitVersion
Another approach to determining API availability is to check framework version numbers. Unfortunately, Foundation's `NSFoundationVersionNumber` and Core Foundation's `kCFCoreFoundationVersionNumber` have historically been out of date, missing constants for past OS releases.
-This is a dead-end for iOS, but OS X can pretty reliably check against the version of AppKit, with `NSAppKitVersionNumber`:
+This is a dead-end for iOS, but macOS can pretty reliably check against the version of AppKit, with `NSAppKitVersion`:
-~~~{swift}
-if rint(NSAppKitVersionNumber) > NSAppKitVersionNumber10_9 {
- println("OS X >= 10.10")
+```swift
+if NSAppKitVersion.current.rawValue >= .macOS10_10.rawValue {
+ println("macOS >= 10.10")
}
-~~~
+```
-> Apple uses `rint` in sample code to round off version numbers for `NSAppKitVersionNumber` comparison.
+If you pair this with an extension to make `NSAppKitVersion` conform to `Comparable`, you can remove the `.rawValue`s as well.
* * *
To summarize, here's what you need to know about checking the system version in Swift:
-- Use `#if os(iOS)` preprocessor directives to distinguish between iOS (UIKit) and OS X (AppKit) targets.
-- For minimum deployment targets of iOS 8.0 or above, use `NSProcessInfo` `operatingSystemVersion` or `isOperatingSystemAtLeastVersion`.
-- For minimum deployment targets of iOS 7.1 or below, use `compare` with `NSStringCompareOptions.NumericSearch` on `UIDevice` `systemVersion`.
-- For OS X deployment targets, compare `NSAppKitVersionNumber` against available AppKit constants.
+- Use `#if os(iOS)` preprocessor directives to distinguish between iOS (UIKit) and macOS (AppKit) targets.
+- For minimum deployment targets of iOS 8.0 or above, use `ProcessInfo` `operatingSystemVersion` or `isOperatingSystemAtLeast`.
+- For minimum deployment targets of iOS 7.1 or below, use `compare` with `String.CompareOptions.numeric` on `UIDevice` `systemVersion`.
+- For macOS deployment targets, compare `NSAppKitVersion` against available AppKit constants.
diff --git a/2014-10-17-inter-process-communication.md b/2014-10-17-inter-process-communication.md
index c21104e8..c63aafc9 100644
--- a/2014-10-17-inter-process-communication.md
+++ b/2014-10-17-inter-process-communication.md
@@ -1,15 +1,13 @@
---
title: Inter-Process Communication
-author: Mattt Thompson
-category: ""
+author: Mattt
+category: Miscellaneous
tags: cfhipsterref
excerpt: "In many ways, the story of Apple has been about fusing together technologies through happy accidents of history to create something better than before: OS X as a hybrid of MacOS & NeXTSTEP. Objective-C as the combination of Smalltalk's OOP paradigm and C. iCloud as the byproduct of MobileMe and actual clouds (presumably)."
status:
- swift: t.b.c.
+ swift: t.b.c.
---
-
-
In many ways, the story of Apple has been about fusing together technologies through happy accidents of history to create something better than before: OS X as a hybrid of MacOS & NeXTSTEP. Objective-C as the combination of Smalltalk's OOP paradigm and C. iCloud as the byproduct of MobileMe and _actual_ clouds (presumably).
While this is true for many aspects of Apple's technology stack, inter-process communication is a flagrant counter-example.
@@ -33,7 +31,7 @@ Mach ports are light-weight and powerful, but poorly documented
Sending a message over a given Mach port comes down to a single `mach_msg_send` call, but it takes a bit of configuration in order to build the message to be sent:
-~~~{objective-c}
+```objc
natural_t data;
mach_port_t port;
@@ -62,13 +60,13 @@ message.type = (mach_msg_type_descriptor_t) {
mach_msg_return_t error = mach_msg_send(&message.header);
if (error == MACH_MSG_SUCCESS) {
- // ...
+ <#...#>
}
-~~~
+```
Things are a little easier on the receiving end, since the message only needs to be declared, not initialized:
-~~~{objective-c}
+```objc
mach_port_t port;
struct {
@@ -82,26 +80,26 @@ mach_msg_return_t error = mach_msg_receive(&message.header);
if (error == MACH_MSG_SUCCESS) {
natural_t data = message.type.pad1;
- // ...
+ <#...#>
}
-~~~
+```
Fortunately, higher-level APIs for Mach ports are provided by Core Foundation and Foundation. `CFMachPort` / `NSMachPort` are wrappers on top of the kernel APIs that can be used as a runloop source, while `CFMessagePort` / `NSMessagePort` facilitate synchronous communication between two ports.
`CFMessagePort` is actually quite nice for simple one-to-one communication. In just a few lines of code, a local named port can be attached as a runloop source to have a callback run each time a message is received:
-~~~{objective-c}
+```objc
static CFDataRef Callback(CFMessagePortRef port,
SInt32 messageID,
CFDataRef data,
void *info)
{
- // ...
+ <#...#>
}
CFMessagePortRef localPort =
CFMessagePortCreateLocal(nil,
- CFSTR("com.example.app.port.server"),
+ CFSTR("com.example.app.port"),
Callback,
nil,
nil);
@@ -112,18 +110,18 @@ CFRunLoopSourceRef runLoopSource =
CFRunLoopAddSource(CFRunLoopGetCurrent(),
runLoopSource,
kCFRunLoopCommonModes);
-~~~
+```
Sending data is straightforward as well. Just specify the remote port, the message payload, and timeouts for sending and receiving. `CFMessagePortSendRequest` takes care of the rest:
-~~~{objective-c}
+```objc
CFDataRef data;
SInt32 messageID = 0x1111; // Arbitrary
CFTimeInterval timeout = 10.0;
CFMessagePortRef remotePort =
CFMessagePortCreateRemote(nil,
- CFSTR("com.example.app.port.client"));
+ CFSTR("com.example.app.port"));
SInt32 status =
CFMessagePortSendRequest(remotePort,
@@ -134,11 +132,11 @@ SInt32 status =
NULL,
NULL);
if (status == kCFMessagePortSuccess) {
- // ...
+ <#...#>
}
-~~~
+```
-## Distributed Notifications
+## Distributed Notifications
There are many ways for objects to communicate with one another in Cocoa:
@@ -148,14 +146,14 @@ Each application manages its own `NSNotificationCenter` instance for infra-appli
To listen for notifications, add an observer to the distributed notification center by specifying the notification name to listen for, and a function pointer to execute each time a notification is received:
-~~~{objective-c}
+```objc
static void Callback(CFNotificationCenterRef center,
void *observer,
CFStringRef name,
const void *object,
CFDictionaryRef userInfo)
{
- // ...
+ <#...#>
}
CFNotificationCenterRef distributedCenter =
@@ -170,11 +168,11 @@ CFNotificationCenterAddObserver(distributedCenter,
CFSTR("notification.identifier"),
NULL,
behavior);
-~~~
+```
Sending a distributed notification is even simpler; just post the identifier, object, and user info:
-~~~{objective-c}
+```objc
void *object;
CFDictionaryRef userInfo;
@@ -186,17 +184,17 @@ CFNotificationCenterPostNotification(distributedCenter,
object,
userInfo,
true);
-~~~
+```
Of all of the ways to link up two applications, distributed notifications are by far the easiest. It wouldn't be a great idea to use them to send large payloads, but for simple tasks like synchronizing preferences or triggering a data fetch, distributed notifications are perfect.
-## Distributed Objects
+## Distributed Objects
Distributed Objects (DO) is a remote messaging feature of Cocoa that had its heyday back in the mid-90's with NeXT. And though its not widely used any more, the dream of totally frictionless IPC is still unrealized in our modern technology stack.
Vending an object with DO is just a matter of setting up an `NSConnection` and registering it with a particular name:
-~~~{objective-c}
+```objc
@protocol Protocol;
id vendedObject;
@@ -204,14 +202,14 @@ id vendedObject;
NSConnection *connection = [[NSConnection alloc] init];
[connection setRootObject:vendedObject];
[connection registerName:@"server"];
-~~~
+```
Another application would then create a connection registered for that same registered name, and immediately get an atomic proxy that functioned as if it were that original object:
-~~~{objective-c}
+```objc
id proxy = [NSConnection rootProxyForConnectionWithRegisteredName:@"server" host:nil];
[proxy setProtocolForProxy:@protocol(Protocol)];
-~~~
+```
Any time a distributed object proxy is messaged, a Remote Procedure Call (RPC) would be made over the `NSConnection` to evaluate the message against the vended object and return the result back to the proxy.
@@ -231,7 +229,7 @@ All that's really left are traces of the annotations used by Distributed Objects
- `bycopy`: Return a copy of the object
- `byref`: Return a proxy of the object
-## AppleEvents & AppleScript
+## AppleEvents & AppleScript
AppleEvents are the most enduring legacies of the classic Macintosh operating system. Introduced in System 7, AppleEvents allowed apps to be controlled locally using AppleScript, or remotely using a feature called Program Linking. To this day, AppleScript, using the Cocoa Scripting Bridge, remains the most direct way to programmatically interact with OS X applications.
@@ -241,11 +239,11 @@ AppleScript uses a natural language syntax, intended to be more accessible to no
To get a better sense of the nature of the beast, here's how to tell Safari to open a URL in the active tab in the frontmost window:
-~~~{Applescript}
+```applescript
tell application "Safari"
- set the URL of the front document to "http://nshipster.com"
+ set the URL of the front document to "https://nshipster.com"
end tell
-~~~
+```
In many ways, AppleScript's natural language syntax is more of a liability than an asset. English, much like any other spoken language, has a great deal of redundancy and ambiguity built into normal constructions. While this is perfectly acceptable for humans, computers have a tough time resolving all of this.
@@ -253,36 +251,36 @@ Even for a seasoned Objective-C developer, it's nearly impossible to write Apple
Fortunately, the Scripting Bridge provides a proper programming interface for Cocoa applications.
-### Cocoa Scripting Bridge
+### Cocoa Scripting Bridge
In order to interact with an application through the Scripting Bridge, a programming interface must first be generated:
-~~~
+```
$ sdef /Applications/Safari.app | sdp -fh --basename Safari
-~~~
+```
`sdef` generates scripting definition files for an application. These files can then be piped into `sdp` to be converted into another format—in this case, a C header. The resulting `.h` file can then be added and `#import`-ed into a project to get a first-class object interface to that application.
Here's the same example as before, expressed using the Cocoa Scripting Bridge:
-~~~{objective-c}
+```objc
#import "Safari.h"
SafariApplication *safari = [SBApplication applicationWithBundleIdentifier:@"com.apple.Safari"];
for (SafariWindow *window in safari.windows) {
if (window.visible) {
- window.currentTab.URL = [NSURL URLWithString:@"http://nshipster.com"];
+ window.currentTab.URL = [NSURL URLWithString:@"https://nshipster.com"];
break;
}
}
-~~~
+```
It's a bit more verbose than AppleScript, but this is much easier to integrate into an existing codebase. It's also a lot clearer to understand how this same code could be adapted to slightly different behavior (though that could just be the effect of being more familiar with Objective-C).
Alas, AppleScript's star appears to be falling, as recent releases of OS X and iWork applications have greatly curtailed their scriptability. At this point, it's unlikely that adding support in your own applications will be worth it.
-## Pasteboard
+## Pasteboard
Pasteboard is the most visible inter-process communication mechanism on OS X and iOS. Whenever a user copies or pastes a piece of text, an image, or a document between applications, an exchange of data from one process to another over mach ports is being mediated by the `com.apple.pboard` service.
@@ -290,24 +288,24 @@ On OS X there's `NSPasteboard`, and on iOS there's `UIPasteboard`. They're prett
Programmatically writing to the Pasteboard is nearly as simple as invoking `Edit > Copy` in a GUI application:
-~~~{objective-c}
+```objc
NSImage *image;
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
[pasteboard clearContents];
[pasteboard writeObjects:@[image]];
-~~~
+```
The reciprocal paste action is a bit more involved, requiring an iteration over the Pasteboard contents:
-~~~{objective-c}
+```objc
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
if ([pasteboard canReadObjectForClasses:@[[NSImage class]] options:nil]) {
NSArray *contents = [pasteboard readObjectsForClasses:@[[NSImage class]] options: nil];
NSImage *image = [contents firstObject];
}
-~~~
+```
What makes Pasteboard especially compelling as a mechanism for transferring data is the notion of simultaneously providing multiple representations of content copied onto a pasteboard. For example, a selection of text may be copied as both rich text (RTF) and plain text (TXT), allowing, for example, a WYSIWYG editor to preserve style information by grabbing the rich text representation, and a code editor to use just the plain text representation.
@@ -315,7 +313,7 @@ These representations can even be provided on an on-demand basis by conforming t
Each representation is identified by a Unique Type Identifier (UTI), a concept discussed in greater detail in the next chapter.
-## XPC
+## XPC
XPC is the state-of-the-art for inter-process communication in the SDKs. Its architectural goals are to avoid long-running process, to adapt to the available resources, and to lazily initialize wherever possible. The motivation to incorporate XPC into an application is not to do things that are otherwise impossible, but to provide better privilege separation and fault isolation for inter-process communication.
@@ -323,9 +321,9 @@ It's a replacement for `NSTask`, and a whole lot more.
Introduced in 2011, XPC has provided the infrastructure for the App Sandbox on OS X, Remote View Controllers on iOS, and App Extensions on both. It is also widely used by system frameworks and first-party applications:
-~~~{bash}
+```bash
$ find /Applications -name \*.xpc
-~~~
+```
By surveying the inventory of XPC services in the wild, one can get a much better understanding of opportunities to use XPC in their own application. Common themes in applications emerge, like services for image and video conversion, system calls, webservice integration, and 3rd party authentication.
@@ -339,7 +337,7 @@ XPC services either reside within an application bundle or are advertised to run
Services call `xpc_main` with an event handler to receive new XPC connections:
-~~~{objective-c}
+```objc
static void connection_handler(xpc_connection_t peer) {
xpc_connection_set_event_handler(peer, ^(xpc_object_t event) {
peer_event_handler(peer, event);
@@ -352,28 +350,28 @@ int main(int argc, const char *argv[]) {
xpc_main(connection_handler);
exit(EXIT_FAILURE);
}
-~~~
+```
-Each XPC connection is one-to-one, meaning that the service operates on distinct connections, with each call to `xpc_connection_create` creating a new peer. :
+Each XPC connection is one-to-one, meaning that the service operates on distinct connections, with each call to `xpc_connection_create` creating a new peer. :
-~~~{objective-c}
+```objc
xpc_connection_t c = xpc_connection_create("com.example.service", NULL);
xpc_connection_set_event_handler(c, ^(xpc_object_t event) {
- // ...
+ <#...#>
});
xpc_connection_resume(c);
-~~~
+```
When a message is sent over an XPC connection, it is automatically dispatched onto a queue managed by the runtime. As soon as the connection is opened on the remote end, messages are dequeued and sent.
Each message is a dictionary, with string keys and strongly-typed values:
-~~~{objective-c}
+```objc
xpc_dictionary_t message = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_uint64(message, "foo", 1);
xpc_connection_send_message(c, message);
xpc_release(message)
-~~~
+```
XPC objects operate on the following primitive types:
@@ -391,7 +389,7 @@ XPC objects operate on the following primitive types:
XPC offers a convenient way to convert from the `dispatch_data_t` data type, which simplifies the workflow from GCD to XPC:
-~~~{objective-c}
+```objc
void *buffer;
size_t length;
dispatch_data_t ddata =
@@ -401,26 +399,26 @@ dispatch_data_t ddata =
DISPATCH_DATA_DESTRUCTOR_MUNMAP);
xpc_object_t xdata = xpc_data_create_with_dispatch_data(ddata);
-~~~
+```
-~~~{objective-c}
+```objc
dispatch_queue_t queue;
xpc_connection_send_message_with_reply(c, message, queue,
^(xpc_object_t reply)
{
if (xpc_get_type(event) == XPC_TYPE_DICTIONARY) {
- // ...
+ <#...#>
}
});
-~~~
+```
-### Registering Services
+### Registering Services
XPC can also be registered as launchd jobs, configured to automatically start on matching IOKit events, BSD notifications or CFDistributedNotifications. These criteria are specified in a service's `launchd.plist` file:
.launchd.plist
-~~~{xml}
+```xml
LaunchEventscom.apple.iokit.matching
@@ -440,14 +438,14 @@ XPC can also be registered as launchd jobs, configured to automatically start on
-~~~
+```
A recent addition to `launchd` property lists is the `ProcessType` key, which describe at a high level the intended purpose of the launch agent. Based on the prescribed contention behavior, the operating system will automatically throttle CPU and I/O bandwidth accordingly.
#### Process Types and Contention Behavior
| Process Type | Contention Behavior |
-|--------------|---------------------------------------------------|
+| ------------ | ------------------------------------------------- |
| Standard | Default value |
| Adaptive | Contend with apps when doing work on their behalf |
| Background | Never contend with apps |
@@ -455,7 +453,7 @@ A recent addition to `launchd` property lists is the `ProcessType` key, which de
To register a service to run approximately every 5 minutes (allowing a grace period for system resources to become more available before scheduling at a more aggressive priority), a set of criteria is passed into `xpc_activity_register`:
-~~~{objective-c}
+```objc
xpc_object_t criteria = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_int64(criteria, XPC_ACTIVITY_INTERVAL, 5 * 60);
xpc_dictionary_set_int64(criteria, XPC_ACTIVITY_GRACE_PERIOD, 10 * 60);
@@ -474,4 +472,4 @@ xpc_activity_register("com.example.app.activity",
xpc_activity_set_state(activity, XPC_ACTIVITY_STATE_DONE);
});
});
-~~~
+```
diff --git a/2014-10-28-cmdevicemotion.md b/2014-10-28-cmdevicemotion.md
index cb72f168..7fcc527a 100644
--- a/2014-10-28-cmdevicemotion.md
+++ b/2014-10-28-cmdevicemotion.md
@@ -1,233 +1,387 @@
---
title: CMDeviceMotion
-author: Nate Cook
+author: Nate Cook, Mattt
category: Cocoa
-excerpt: "Beneath the smooth glass of each shiny iPhone, nestled on a logic board between touch screen controllers and Apple-designed SoCs, the gyroscope and accelerometer sit largely neglected."
+excerpt: >
+ Beneath the smooth glass of each iPhone
+ an array of sensors sits nestled on the logic board,
+ sending a steady stream of data to a motion coprocessor.
+revisions:
+ "2014-10-28": Original publication
+ "2018-09-12": Updated for Swift 4.2
status:
- swift: 2.2
- reviewed: April 10, 2016
+ swift: 4.2
+ reviewed: September 12, 2018
---
-Beneath the smooth glass of each shiny iPhone, nestled on a logic board between touch screen controllers and Apple-designed SoCs, the gyroscope and accelerometer sit largely neglected.
+Beneath the smooth glass of each iPhone
+an array of sensors sits nestled on the logic board,
+sending a steady stream of data to a motion coprocessor.
-Need it be so? The *[Core Motion framework](https://developer.apple.com/library/ios/documentation/coremotion/reference/coremotion_reference/index.html)* makes it surprisingly easy to harness these sensors, opening the door to user interactions above and beyond the tapping and swiping we do every day.
-
-> For devices that include the M7 or M8 motion processor, the Core Motion framework also provides access to stored motion activity, such as step counts, stairs climbed, and movement type (walking, cycling, etc.).
+The [Core Motion framework](https://developer.apple.com/documentation/coremotion)
+makes it surprisingly easy to harness these sensors,
+opening the door to user interactions above and beyond
+the tapping and swiping we do every day.
---
-Core Motion allows a developer to observe and respond to the motion and orientation of an iOS device by inspecting the raw and processed data from a combination of built-in sensors, including the accelerometer, gyroscope, and magnetometer.
+Core Motion lets you observe and respond to changes in
+the position and orientation of an iOS or watchOS device.
+Thanks to their dedicated motion coprocessor,
+iPhones, iPads, and Apple Watches can continuously read and process inputs
+from built-in sensors without taxing the CPU or draining the battery.
+
+Accelerometer and gyroscope data is projected into a 3D coordinate space,
+with the center of the device at the origin.
-Both accelerometer and gyroscope data are presented in terms of three axes that run through the iOS device. For an iPhone held in portrait orientation, the X-axis runs through the device from left (negative values) to right (positive values), the Y-axis through the device from bottom (-) to top (+), and the Z-axis runs perpendicularly through the screen from the back (-) to the front (+).
+
-The composited device motion data are presented in a few different ways, each with their own uses, as we'll see below.
+For an iPhone held in portrait orientation:
-
+- The X-axis runs the width of the device
+ from left (negative values) to right (positive values),
+- The Y-axis runs the height of the device
+ from bottom (-) to top (+),
+- The Z-axis runs perpendicularly through the screen
+ from the back (-) to the front (+).
## CMMotionManager
-The `CMMotionManager` class provides access to all the motion data on an iOS device. Interestingly, Core Motion provides both "pull" and "push" access to motion data. To "pull" motion data, you can access the current status of any sensor or the composited data as read-only properties of `CMMotionManager`. To receive "pushed" data, you start the collection of your desired data with a block or closure that receives updates at a specified interval.
+The `CMMotionManager` class is responsible for
+providing data about the motion of the current device.
+To keep performance at the highest level,
+create and use a single shared `CMMotionManager` instance throughout your app.
+
+`CMMotionManager` provides four different interfaces for sensor information,
+each with corresponding properties and methods
+to check hardware availability and access measurements.
+
+- The accelerometer measures acceleration,
+ or changes in velocity over time.
+- The gyroscope measures attitude,
+ or the orientation of the device.
+- The magnetometer is essentially a compass,
+ and measures the Earth's magnetic field relative to the device.
+
+In addition to these individual readings,
+`CMMotionManager` also provides a unified "device motion" interface,
+which uses sensor fusion algorithms to combine readings
+from each of these sensors into a unified view of the device in space.
-To keep performance at the highest level, Apple recommends using a single shared `CMMotionManager` instance throughout your app.
+### Checking for Availability
-`CMMotionManager` provides a consistent interface for each of the four motion data types: `accelerometer`, `gyro`, `magnetometer`, and `deviceMotion`. As an example, here are the ways you can interact with the gyroscope—simply replace `gyro` with the motion data type you need.
+Although most Apple devices these days
+come with a standard set of sensors,
+it's still a good idea to check for the capabilities of the current device
+before attempting to read motion data.
-#### Checking for Availability
+The following examples involve the accelerometer,
+but you could replace the word "accelerometer"
+for the type of motion data that you're interested in
+(such as "gyro", "magnetometer", or "deviceMotion"):
```swift
let manager = CMMotionManager()
-if manager.gyroAvailable {
- // ...
+guard manager.isAccelerometerAvailable else {
+ return
}
```
-> To make things simpler and equivalent between Swift and Objective-C, assume we've declared a `manager` instance as a view controller property for all the examples to come.
+{% comment %}
-#### Setting the Update Interval
+> Too keep things concise,
+> assume that each of the following examples declares
+> a `manager` instance as a view controller property.
-```swift
-manager.gyroUpdateInterval = 0.1
-```
+{% endcomment %}
+
+### Push vs. Pull
+
+Core Motion provides both "pull" and "push" access to motion data.
+
+To "pull" motion data,
+you access the current reading from
+using one of the read-only properties of `CMMotionManager`.
-This is an `NSTimeInterval`, so specify your update time in seconds: lower for smoother responsiveness, higher for less CPU usage.
+To receive "pushed" data,
+you start the collection of your desired data
+with a closure that receives updates at a specified interval.
#### Starting Updates to "pull" Data
```swift
-manager.startGyroUpdates()
+manager.startAccelerometerUpdates()
```
-After this call, `manager.gyroData` is accessible at any time with the device's current gyroscope data.
+After this call,
+`manager.accelerometerData` is accessible at any time
+with the device's current accelerometer data.
+
+```swift
+manager.accelerometerData
+```
+
+You can also check whether motion data is available by
+reading the corresponding "is active" property.
+
+```swift
+manager.isAccelerometerActive
+```
#### Starting Updates to "push" Data
```swift
-let queue = NSOperationQueue.mainQueue()
-manager.startGyroUpdatesToQueue(queue) {
- (data, error) in
- // ...
+manager.startAccelerometerUpdates(to: .main) { (data, error) in
+ guard let data = data, error == nil else {
+ return
+ }
+
+ <#...#>
}
```
-The handler closure will be called at the frequency given by the update interval.
+The passed closure is called at the frequency provided by the update interval.
+(Actually, Core Motion enforces a minimum and maximum frequency,
+so specifying a value outside of that range causes that value to be normalized;
+you can determine the effective interval rate of the current device
+by checking the timestamps of motion events over time.)
#### Stopping Updates
```swift
-manager.stopGyroUpdates()
+manager.stopAccelerometerUpdates()
```
-## Using the Accelerometer
+## Accelerometer in Action
-Let's say we want to give the splash page of our app a fun effect, with the background image staying level no matter how the phone is tilted.
+Let's say we want to give the splash page of our app a fun effect,
+such that the background image remains level no matter how the phone is tilted.
Consider the following code:
-First, we check to make sure our device makes accelerometer data available, next we specify a very high update rate, and then we begin updates to a closure that will rotate a `UIImageView` property:
-
```swift
-if manager.accelerometerAvailable {
+if manager.isAccelerometerAvailable {
manager.accelerometerUpdateInterval = 0.01
- manager.startAccelerometerUpdatesToQueue(NSOperationQueue.mainQueue()) {
- [weak self] (data: CMAccelerometerData?, error: NSError?) in
- if let acceleration = data?.acceleration {
- let rotation = atan2(acceleration.x, acceleration.y) - M_PI
- self?.imageView.transform = CGAffineTransformMakeRotation(CGFloat(rotation))
+ manager.startAccelerometerUpdates(to: .main) {
+ [weak self] (data, error) in
+ guard let data = data, error == nil else {
+ return
}
- }
-}
-```
-```objective-c
-RotationViewController * __weak weakSelf = self;
-if (manager.accelerometerAvailable) {
- manager.accelerometerUpdateInterval = 0.01f;
- [manager startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue]
- withHandler:^(CMAccelerometerData *data, NSError *error) {
- double rotation = atan2(data.acceleration.x, data.acceleration.y) - M_PI;
- weakSelf.imageView.transform = CGAffineTransformMakeRotation(rotation);
- }];
+ let rotation = atan2(data.acceleration.x,
+ data.acceleration.y) - .pi
+ self?.imageView.transform =
+ CGAffineTransform(rotationAngle: CGFloat(rotation))
+ }
}
```
-Each packet of `CMAccelerometerData` includes an `x`, `y`, and `z` value—each of these shows the amount of acceleration in Gs (where G is one unit of gravity) for that axis. That is, if your device were stationary and straight up in portrait orientation, it would have acceleration `(0, -1, 0)`; laying flat on its back on the table would be `(0, 0, -1)`; and tilted forty-five degrees to the right would be something like `(0.707, -0.707, 0)`.
-
-We're calculating the rotation by computing the [`arctan2`](http://en.wikipedia.org/wiki/Atan2) of the `x` and `y` components from the accelerometer data and then using that rotation in a `CGAffineTransform`. Our image should stay right-side up no matter how the phone is turned—here it is in a hypothetical app for the *National Air & Space Museum* (my favorite museum as a kid):
-
-
-
-The results are not terribly satisfactory—the image movement is jittery, and moving the device in space affects the accelerometer as much as or even more than rotating. These issues *could* be mitigated by sampling multiple readings and averaging them together, but instead let's look at what happens when we involve the gyroscope.
-
-
+First, we check to make sure our device makes accelerometer data available.
+Next we specify a high update frequency.
+And then finally,
+we begin updates to a closure that will rotate a `UIImageView` property:
+
+Each `CMAccelerometerData` object includes an `x`, `y`, and `z` value ---
+each of these shows the amount of acceleration in G-forces
+(where 1G = the force of gravity on Earth)
+for that axis.
+If your device were stationary and standing straight up in portrait orientation,
+it would have acceleration `(0, -1, 0)`;
+laying flat on its back on the table,
+it would be `(0, 0, -1)`;
+tilted forty-five degrees to the right,
+it would be something like `(0.707, -0.707, 0)` _(dat √2 tho)_.
+
+We calculate the rotation with the
+[two-argument arctangent function (`atan2`)](https://en.wikipedia.org/wiki/Atan2)
+using the `x` and `y` components from the accelerometer data.
+We then initialize a `CGAffineTransform` using that calculate rotation.
+Our image should stay right-side-up, no matter how the phone is turned ---
+here, it is in a hypothetical app for the _National Air & Space Museum_
+(my favorite museum as a kid):
+
+
+
+The results are not terribly satisfactory ---
+the image movement is jittery,
+and moving the device in space affects the accelerometer
+as much as or even more than rotating.
+These issues _could_ be mitigated by
+sampling multiple readings and averaging them together,
+but instead let's look at what happens when we involve the gyroscope.
## Adding the Gyroscope
-Rather than use the raw gyroscope data that we would get with `startGyroUpdates...`, let's get composited gyroscope *and* accelerometer data from the `deviceMotion` data type. Using the gyroscope, Core Motion separates user movement from gravitational acceleration and presents each as its own property of the `CMDeviceMotion` instance that we receive in our handler. The code is very similar to our first example:
+Rather than use the raw gyroscope data that we would get
+by calling the `startGyroUpdates...` method,
+let's get composited gyroscope _and_ accelerometer data
+by requesting the unified "device motion" data.
+Using the gyroscope,
+Core Motion separates user movement from gravitational acceleration
+and presents each as its own property of the `CMDeviceMotion` object.
+The code is very similar to our first example:
```swift
-if manager.deviceMotionAvailable {
+if manager.isDeviceMotionAvailable {
manager.deviceMotionUpdateInterval = 0.01
- manager.startDeviceMotionUpdatesToQueue(NSOperationQueue.mainQueue()) {
- [weak self] (data: CMDeviceMotion?, error: NSError?) {
- if let gravity = data?.gravity {
- let rotation = atan2(data.gravity.x, data.gravity.y) - M_PI
- self?.imageView.transform = CGAffineTransformMakeRotation(CGFloat(rotation))
+ manager.startDeviceMotionUpdates(to: .main) {
+ [weak self] (data, error) in
+
+ guard let data = data, error == nil else {
+ return
}
- }
-}
-```
-```objective-c
-RotationViewController * __weak weakSelf = self;
-if (manager.deviceMotionAvailable) {
- manager.deviceMotionUpdateInterval = 0.01f;
- [manager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue]
- withHandler:^(CMDeviceMotion *data, NSError *error) {
- double rotation = atan2(data.gravity.x, data.gravity.y) - M_PI;
- weakSelf.imageView.transform = CGAffineTransformMakeRotation(rotation);
- }];
+ let rotation = atan2(data.gravity.x,
+ data.gravity.y) - .pi
+ self?.imageView.transform =
+ CGAffineTransform(rotationAngle: CGFloat(rotation))
+ }
}
```
-Much better!
+_Much better!_
-
+
## UIClunkController
-We can also use the other, non-gravity portion of this composited gyro/acceleration data to add new methods of interaction. In this case, let's use the `userAcceleration` property of `CMDeviceMotion` to navigate backward whenever a user taps the left side of her device against her hand.
+We can also use the other, non-gravity portion
+of this composited gyro / acceleration data
+to add new methods of interaction.
+In this case, let's use the `userAcceleration` property of `CMDeviceMotion`
+to navigate backward whenever
+the user taps the left side of the device against their hand.
-Remember that the X-axis runs laterally through the device in our hand, with negative values to the left. If we sense a *user* acceleration to the left of more than 2.5 Gs, that will be the cue to pop our view controller from the stack. The implementation is only a couple lines different from our previous example:
+Remember that the X-axis runs laterally through the device in our hand,
+with negative values to the left.
+If we sense a _user_ acceleration to the left of more than 2.5 Gs,
+that's our cue to pop the view controller from the stack.
+The implementation is only a couple lines different from our previous example:
```swift
-if manager.deviceMotionAvailable {
- manager.deviceMotionUpdateInterval = 0.02
- manager.startDeviceMotionUpdatesToQueue(NSOperationQueue.mainQueue()) {
- [weak self] (data: CMDeviceMotion?, error: NSError?) in
+if manager.isDeviceMotionAvailable {
+ manager.deviceMotionUpdateInterval = 0.01
+ manager.startDeviceMotionUpdates(to: .main) {
+ [weak self] (data, error) in
- if data?.userAcceleration.x < -2.5 {
+ guard let data = data, error == nil else {
+ return
+ }
+ if data.userAcceleration.x < -2.5 {
self?.navigationController?.popViewControllerAnimated(true)
}
}
}
```
-```objective-c
-ClunkViewController * __weak weakSelf = self;
-if (manager.deviceMotionAvailable) {
- manager.deviceMotionUpdateInterval = 0.01f;
- [manager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue]
- withHandler:^(CMDeviceMotion *data, NSError *error) {
- if (data.userAcceleration.x < -2.5f) {
- [weakSelf.navigationController popViewControllerAnimated:YES];
- }
- }];
-}
-```
-
-And it works like a charm—tapping the device in a detail view immediately takes us back to the list of exhibits:
-
-
+_Works like a charm!_
+Tapping the device in a detail view
+immediately takes us back to the list of exhibits:
+
## Getting an Attitude
-Better acceleration data isn't the only thing we gain by including gyroscope data—we now also know the device's true orientation in space. We find this data in the `attitude` property of `CMDeviceMotion`, an instance of `CMAttitude`. `CMAttitude` contains three different representations of the device's orientation: Euler angles, a quaternion, and a rotation matrix. Each of these is in relation to a given reference frame.
+Better acceleration data isn't the only thing we gain
+by including gyroscope data:
+we now also know the device's true orientation in space.
+This data is accessed via the `attitude` property of a `CMDeviceMotion` object
+and encapsulated in a `CMAttitude` object.
+`CMAttitude` contains three different representations of the device's orientation:
-### Finding a Frame of Reference
+- Euler angles,
+- A quaternion,
+- A rotation matrix.
-You can think of a reference frame as the resting orientation of the device from which an attitude is calculated. All four possible reference frames describe the device laying flat on a table, with increasing specificity about the direction it's pointing.
+Each of these is in relation to a given reference frame.
-- `CMAttitudeReferenceFrameXArbitraryZVertical` describes a device laying flat (vertical Z-axis) with an "arbitrary" X-axis. In practice, the X-axis is fixed to the orientation of the device when you *first* start device motion updates.
-- `CMAttitudeReferenceFrameXArbitraryCorrectedZVertical` is essentially the same but uses the magnetometer to correct possible variation in the gyroscope's measurement over time. Using the magnetometer adds a CPU (and therefore battery) cost.
-- `CMAttitudeReferenceFrameXMagneticNorthZVertical` describes a device laying flat, with its X-axis (i.e., the right side of the device) pointed toward magnetic north. This setting may require your user to perform that figure-eight motion with their device to calibrate the magnetometer.
-- `CMAttitudeReferenceFrameXTrueNorthZVertical` is the same as the last, but this adjusts for the magnetic/true north discrepancy and therefore requires location data in addition to the magnetometer.
+### Finding a Frame of Reference
-For our purposes, the default "arbitrary" reference frame will be fine - you'll see why in a moment.
+You can think of a reference frame as the resting orientation of the device
+from which an attitude is calculated.
+All four possible reference frames describe the device laying flat on a table,
+with increasing specificity about the direction it's pointing.
+
+- `CMAttitudeReferenceFrameXArbitraryZVertical`
+ describes a device laying flat (vertical Z-axis)
+ with an "arbitrary" X-axis.
+ In practice, the X-axis is fixed to the orientation of the device
+ when you _first_ start device motion updates.
+- `CMAttitudeReferenceFrameXArbitraryCorrectedZVertical`
+ is essentially the same,
+ but uses the magnetometer to correct
+ possible variation in the gyroscope's measurement over time.
+- `CMAttitudeReferenceFrameXMagneticNorthZVertical`
+ describes a device laying flat,
+ with its X-axis
+ (that is, the right side of the device in portrait mode when it's facing you)
+ pointed toward magnetic north.
+ This setting may require the user to perform
+ that figure-eight motion with their device to calibrate the magnetometer.
+- `CMAttitudeReferenceFrameXTrueNorthZVertical`
+ is the same as the last,
+ but it adjusts for magnetic / true north discrepancy
+ and therefore requires location data in addition to the magnetometer.
+
+For our purposes,
+the default "arbitrary" reference frame will be fine
+(you'll see why in a moment).
### Euler Angles
-Of the three attitude representations, Euler angles are the most readily understood, as they simply describe rotation around each of the axes we've already been working with. `pitch` is rotation around the X-axis, increasing as the device tilts toward you, decreasing as it tilts away; `roll` is rotation around the Y-axis, decreasing as the device rotates to the left, increasing to the right; and `yaw` is rotation around the (vertical) Z-axis, decreasing clockwise, increasing counter-clockwise.
-
-> Each of these values follows what's called the "right hand rule": make a cupped hand with your thumb pointing up and point your thumb in the direction of any of the three axes. Turns that move toward your fingertips are positive, turns away are negative.
+Of the three attitude representations,
+Euler angles are the most readily understood,
+as they simply describe rotation
+around each of the axes we've already been working with.
+
+- `pitch` is rotation around the X-axis,
+ increasing as the device tilts toward you,
+ decreasing as it tilts away
+- `roll` is rotation around the Y-axis,
+ decreasing as the device rotates to the left,
+ increasing to the right
+- `yaw` is rotation around the (vertical) Z-axis,
+ decreasing clockwise, increasing counter-clockwise.
+
+> Each of these values follows what's called the "right hand rule":
+> make a cupped hand with your thumb pointing up
+> and point your thumb in the direction of any of the three axes.
+> Turns that move toward your fingertips are positive,
+> turns away are negative.
### Keep It To Yourself
-Lastly, let's try using the device's attitude to enable a new interaction for a flash-card app, designed to be used by two study buddies. Instead of manually switching between the prompt and the answer, we'll automatically switch the view as the device turns around, so the quizzer sees the answer while the person being quizzed only sees the prompt.
-
-Figuring out this switch from the reference frame would be tricky. To know which angles to monitor, we would somehow need to take into account the starting orientation of the device and then determine which direction the device is pointing. Instead, we can save a `CMAttitude` instance and use it as the "zero point" for an adjusted set of Euler angles, calling the `multiplyByInverseOfAttitude()` method to translate all future attitude updates.
-
-When the quizzer taps the button to begin the quiz, we first configure the interaction—note the "pull" of the deviceMotion for `initialAttitude`:
+Lastly, let's try using the device's attitude to enable a new interaction
+for a flash-card app designed to be used by two study buddies.
+Instead of manually switching between the prompt and the answer,
+we'll automatically flip the view as the device turns around,
+so the quizzer sees the answer
+while the person being quizzed sees only the prompt.
+
+Figuring out this switch from the reference frame would be tricky.
+To know which angles to monitor,
+we would somehow need to account for the starting orientation of the device
+and then determine which direction the device is pointing.
+Instead, we can save a `CMAttitude` instance
+and use it as the "zero point" for an adjusted set of Euler angles,
+calling the `multiply(byInverseOf:)` method
+to translate all future attitude updates.
+
+When the quizzer taps the button to begin the quiz,
+we first configure the interaction
+(note the "pull" of the deviceMotion for `initialAttitude`):
```swift
// get magnitude of vector via Pythagorean theorem
-func magnitudeFromAttitude(attitude: CMAttitude) -> Double {
- return sqrt(pow(attitude.roll, 2) + pow(attitude.yaw, 2) + pow(attitude.pitch, 2))
+func magnitude(from attitude: CMAttitude) -> Double {
+ return sqrt(pow(attitude.roll, 2) +
+ pow(attitude.yaw, 2) +
+ pow(attitude.pitch, 2))
}
// initial configuration
-var initialAttitude = manager.deviceMotion!.attitude
+var initialAttitude = manager.deviceMotion.attitude
var showingPrompt = false
// trigger values - a gap so there isn't a flicker zone
@@ -235,125 +389,92 @@ let showPromptTrigger = 1.0
let showAnswerTrigger = 0.8
```
-```objective-c
-// --- class method to get magnitude of vector via Pythagorean theorem
-+ (double)magnitudeFromAttitude:(CMAttitude *)attitude {
- return sqrt(pow(attitude.roll, 2.0f) + pow(attitude.yaw, 2.0f) + pow(attitude.pitch, 2.0f));
-}
-
-// --- In @IBAction handler
-// initial configuration
-CMAttitude *initialAttitude = manager.deviceMotion.attitude;
-__block BOOL showingPrompt = NO;
-
-// trigger values - a gap so there isn't a flicker zone
-double showPromptTrigger = 1.0f;
-double showAnswerTrigger = 0.8f;
-```
-
-Then, in our now familiar call to `startDeviceMotionUpdates`, we calculate the magnitude of the vector described by the three Euler angles and use that as a trigger to show or hide the prompt view:
+Then,
+in our now familiar call to `startDeviceMotionUpdates`,
+we calculate the magnitude of the vector described by the three Euler angles
+and use that as a trigger to show or hide the prompt view:
```swift
-if manager.deviceMotionAvailable {
- manager.startDeviceMotionUpdatesToQueue(NSOperationQueue.mainQueue()) {
- [weak self] (data: CMDeviceMotion?, error: NSError?) in
-
- guard let data = data else { return }
-
+if manager.isDeviceMotionAvailable {
+ manager.startDeviceMotionUpdates(to: .main) {
// translate the attitude
- data.attitude.multiplyByInverseOfAttitude(initialAttitude)
+ data.attitude.multiply(byInverseOf: initialAttitude)
// calculate magnitude of the change from our initial attitude
- let magnitude = magnitudeFromAttitude(data.attitude) ?? 0
+ let magnitude = magnitude(from: data.attitude) ?? 0
// show the prompt
if !showingPrompt && magnitude > showPromptTrigger {
- if let promptViewController = self?.storyboard?.instantiateViewControllerWithIdentifier("PromptViewController") as? PromptViewController {
+ if let promptViewController =
+ self?.storyboard?.instantiateViewController(
+ withIdentifier: "PromptViewController"
+ ) as? PromptViewController
+ {
showingPrompt = true
- promptViewController.modalTransitionStyle = UIModalTransitionStyle.CrossDissolve
- self?.presentViewController(promptViewController, animated: true, completion: nil)
+ promptViewController.modalTransitionStyle = .crossDissolve
+ self?.present(promptViewController,
+ animated: true, completion: nil)
}
}
// hide the prompt
if showingPrompt && magnitude < showAnswerTrigger {
showingPrompt = false
- self?.dismissViewControllerAnimated(true, completion: nil)
+ self?.dismiss(animated: true, completion: nil)
}
}
}
```
-```objective-c
-FacingViewController * __weak weakSelf = self;
-if (manager.deviceMotionAvailable) {
- [manager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue]
- withHandler:^(CMDeviceMotion *data, NSError *error) {
-
- // translate the attitude
- [data.attitude multiplyByInverseOfAttitude:initialAttitude];
-
- // calculate magnitude of the change from our initial attitude
- double magnitude = [FacingViewController magnitudeFromAttitude:data.attitude];
-
- // show the prompt
- if (!showingPrompt && (magnitude > showPromptTrigger)) {
- showingPrompt = YES;
-
- PromptViewController *promptViewController = [weakSelf.storyboard instantiateViewControllerWithIdentifier:@"PromptViewController"];
- promptViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
- [weakSelf presentViewController:promptViewController animated:YES completion:nil];
- }
-
- // hide the prompt
- if (showingPrompt && (magnitude < showAnswerTrigger)) {
- showingPrompt = NO;
- [weakSelf dismissViewControllerAnimated:YES completion:nil];
- }
- }];
-}
-```
-
-Having implemented all that, let's take a look at the interaction. As the device rotates, the display automatically switches views and the quizee never sees the answer:
+Having implemented all that,
+let's take a look at the interaction.
+As the device rotates,
+the display automatically switches views and the quizee never sees the answer:
-
+
### Further Reading
-I skimmed over the [quaternion](http://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation) and [rotation matrix](http://en.wikipedia.org/wiki/Rotation_matrix) components of `CMAttitude` earlier, but they are not without intrigue. The quaternion, in particular, has [an interesting history](http://en.wikipedia.org/wiki/History_of_quaternions), and will bake your noodle if you think about it long enough.
-
+I skimmed over the
+[quaternion](https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation) and
+[rotation matrix](https://en.wikipedia.org/wiki/Rotation_matrix)
+components of `CMAttitude` earlier,
+but they are not without intrigue.
+The quaternion, in particular,
+has [an interesting history](https://en.wikipedia.org/wiki/History_of_quaternions),
+and will bake your noodle if you think about it long enough.
## Queueing Up
-To keep the code examples readable, we've been sending all our `CoreMotionManager` updates to the main queue. As a best practice, it would be better to have these updates on their own queue so they can't slow down user interaction, but then we'll need to get back on the main queue to update user interface elements. [`NSOperationQueue`](http://nshipster.com/nsoperation/) makes this easy with its `addOperationWithBlock` method:
+To keep the code examples readable,
+we've been sending all of our motion updates to the main queue.
+A better approach would be to schedule these updates on their own queue
+and dispatch back to main to update the UI.
```swift
-let queue = NSOperationQueue()
-manager.startDeviceMotionUpdatesToQueue(queue) {
- [weak self] (data: CMDeviceMotion?, error: NSError?) in
+let queue = OperationQueue()
+manager.startDeviceMotionUpdates(to: queue) {
+ [weak self] (data, error) in
// motion processing here
- NSOperationQueue.mainQueue().addOperationWithBlock {
+ DispatchQueue.main.async {
// update UI here
}
}
```
-```objective-c
-NSOperationQueue *queue = [[NSOperationQueue alloc] init];
-[manager startDeviceMotionUpdatesToQueue:queue
- withHandler:
-^(CMDeviceMotion *data, NSError *error) {
- // motion processing here
-
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- // update UI here
- }];
-}];
-```
-
-----
+---
-As a final note, clearly not all interactions made possible by Core Motion are good ones. Navigation through motion can be fun but also hard to discover or easy to accidentally trigger; purposeless animations can make it harder to focus on the task at hand. Prudent developers will skip over gimmicks that distract and find ways to use device motion that enrich their apps and delight their users.
+Remember that not all interactions made possible by Core Motion are good ones.
+Navigation through motion can be fun,
+but it can also be
+hard to discover,
+easy to accidentally trigger,
+and may not be accessible to all users.
+Similar to purposeless animations,
+overuse of fancy gestures can make it harder to focus on the task at hand.
+
+Prudent developers will skip over gimmicks that distract
+and find ways to use device motion that enrich apps and delight users.
diff --git a/2014-11-03-uisplitviewcontroller.md b/2014-11-03-uisplitviewcontroller.md
index aeed156b..147ecdd1 100644
--- a/2014-11-03-uisplitviewcontroller.md
+++ b/2014-11-03-uisplitviewcontroller.md
@@ -2,150 +2,255 @@
title: UISplitViewController
author: Natasha Murashev
category: Cocoa
-excerpt: "The introduction of iPhone 6+ brought on a new importance for UISplitViewController. With just a few little tweaks, an app can now become Universal, with Apple handling most of the UI logic for all the different screen sizes."
+excerpt: >
+ Although user interface idioms have made way
+ for the broader concept of size classes,
+ `UISplitViewController` remains a workhorse API for writing Universal apps.
+revisions:
+ "2014-11-03": Original publication
+ "2018-09-26": Updated for iOS 12 and Swift 4.2
status:
- swift: 2.0
- reviewed: September 11, 2015
+ swift: 4.2
+ reviewed: September 26, 2018
---
-The introduction of iPhone 6+ brought on a new importance for `UISplitViewController`. With just a few little tweaks, an app can now become Universal, with Apple handling most of the UI logic for all the different screen sizes.
+In the beginning, there was the iPhone.
+And it was good.
-Check out the `UISplitViewController` doing its magic on iPhone 6+:
+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.
-> Note that the view does not split when the iPhone 6+ is in _Zoomed_ Display mode! (You can change between Standard and Zoomed Display Mode by going to Settings.app → Display & Brightness → View)
+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.
-Again, Apple handles the logic for figuring out exactly when to show the split views.
+---
+
+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.
+
+
+
+{% info do %}
+
+You can change between Standard and Zoomed Display Mode in Settings
+by going to General → Accessibility → Zoom,
+enabling the Zoom option
+and selecting Full Screen Zoom for Zoom Region.
-## The Storyboard Layout
+{% endinfo %}
-Here is an overview of what a storyboard layout looks like with a split view controller:
+This is one instance of how split views automatically determine when to show split views.
-
+## Split View Controller, from Start to Finish
-Let's get into more detail:
+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](https://github.com/NSHipster/UISplitViewControllerDemo).
+
+### The Storyboard Layout
+
+Here's an overview of what a storyboard layout looks like with a split view controller:
+
+{% asset uisplitviewcontroller-storyboard-layout.png alt="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. Next, specify which view controller is the **Master** and which one is the **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.
-
+{% asset uisplitviewcontroller-master-detail-storyboard.png alt="UISplitViewController Master-Detail Storyboard" %}
-Do this by selecting the appropriate Relationship Segue:
+You can do this by selecting the appropriate Relationship Segue:
-
+{% asset uisplitviewcontroller-relationship-segue.png alt="UISplitViewController Relationship Segue" %}
-The master view controller is usually the navigation controller containing the list view (a `UITableView` in most cases). The detail view controller is the Navigation Controller for the view corresponding to what shows up when the user taps on the list item.
+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 is one last part to making the split view controller work: specifying the "Show Detail" segue:
+There's one last part to making the split view controller work:
+specifying the "Show Detail" segue.
-
+{% asset uisplitviewcontroller-show-detail-segue.png alt="UISplitViewController Show Detail Segue" %}
-In the example below, when the user taps on a cell in the `SelectColorTableViewController`, they'll be shown a navigation controller with the `ColorViewController` at its root.
+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 both the Master and the Detail view controllers have to be navigation controllers—especially since there is a "Show Detail" segue from a table view (which is part of the navigation stack) to the Detail view controller. What if the Detail View Controller didn't start with a Navigation Controller?
-
-
+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?_.
-By all accounts, the app would still work just fine. On an iPhone 6+, the only difference is the lack of a navigation toolbar when the phone is in landscape mode:
+Well, let's see what happens
+when the detail view controller
+doesn't have a navigation controller at its root:
-
+{% asset uisplitviewcontroller-no-detail-navigation-controller.png alt="UISplitViewController No Detail Navigation Controller" %}
-It's not a big deal, unless you do want your navigation bar to show a title. This ends up being a deal-breaker on an iPad.
+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:
-
+{% asset uisplitviewcontroller-no-navigation-bar.png alt="UISplitViewController No Navigation Bar" %})
-Notice that when the iPad app is first opened up, there is no indication that this is a split view controller at all! To trigger the Master view controller, the user has to magically know to swipe left to right.
+It's not a big deal unless want your navigation bar to show a title.
+But this is a deal-breaker on an iPad:
-Even when the navigation controller is in place, the UI is not that much better at first glance (although seeing a title is definitely an improvement):
+
-
+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.
-### `displayModeButtonItem`
+### Adding a Display Mode Button
-The simplest way to fix this issue would be to somehow indicate that there is more to the app than what's currently on-screen. Luckily, the UISplitViewController has a **displayModeButtonItem**, which can be added to the navigation bar:
+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:
-~~~{swift}
+```swift
override func viewDidLoad() {
super.viewDidLoad()
- // ...
+ <#...#>
- navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem()
+ navigationItem.leftBarButtonItem =
+ splitViewController?.displayModeButtonItem
navigationItem.leftItemsSupplementBackButton = true
}
-~~~
-~~~{objective-c}
+```
+
+```objc
- (void)viewDidLoad {
[super viewDidLoad];
- // ...
+ <#...#>
- self.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;
+ self.navigationItem.leftBarButtonItem =
+ self.splitViewController.displayModeButtonItem;
self.navigationItem.leftItemsSupplementBackButton = YES;
}
-~~~
+```
-Build and Run on the iPad again, and now the user gets a nice indication of how to get at the rest of the app:
+_Build and Run_ on the iPad again,
+and now you get a nice indication of how access the rest of the app:
-
+
-`UISplitViewController`'s `displayModeButtonItem` adds a bit of extra-cool usability to the iPhone 6+ in landscape mode, too:
+The `displayModeButtonItem` property lends some nice usability
+to apps running on large iPhones in landscape mode, too:
-
+
-By using the `displayModeButtonItem`, you're once again letting Apple figure out what's appropriate for which screen sizes / rotations. Instead of sweating the small (and big) stuff yourself, you can sit back and relax.
+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 is one more optimization we can do for the iPhone 6+ via [`UISplitViewControllerDelegate`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UISplitViewControllerDelegate_protocol/index.html).
+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`](https://developer.apple.com/documentation/uikit/uisplitviewcontrollerdelegate):
-When the user first launches the app, we can make the master view controller fully displayed until the user selects a list item:
+```swift
+class ColorsViewController: UITableViewController {
+ var collapseDetailViewController: Bool = true
-~~~{swift}
-class SelectColorTableViewController: UITableViewController, UISplitViewControllerDelegate {
- private var collapseDetailViewController = true
-
- override func viewDidLoad() {
- super.viewDidLoad()
-
- splitViewController?.delegate = self
- }
-
- // ...
+ <#...#>
// MARK: - UITableViewDelegate
- override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
- collapseDetailViewController = false
+ override func tableView(_ tableView: UITableView,
+ didSelectRowAt indexPath: IndexPath)
+ {
+ self.collapseDetailViewController = false
}
+}
+
+class SplitViewDelegate: NSObject, UISplitViewControllerDelegate {
+ <#...#>
- // MARK: - UISplitViewControllerDelegate
+ func splitViewController(_ splitViewController: UISplitViewController,
+ collapseSecondary secondaryViewController: UIViewController,
+ onto primaryViewController: UIViewController) -> Bool
+ {
+ guard let navigationController = primaryViewController as? UINavigationController,
+ let controller = navigationController.topViewController as? ColorsViewController
+ else {
+ return true
+ }
- func splitViewController(splitViewController: UISplitViewController, collapseSecondaryViewController secondaryViewController: UIViewController, ontoPrimaryViewController primaryViewController: UIViewController) -> Bool {
- return collapseDetailViewController
+ return controller.collapseDetailViewController
}
}
-~~~
-~~~{objective-c}
+```
+
+```objc
// SelectColorTableViewController.h
-@interface SelectColorTableViewController : UITableViewController
+@interface SelectColorTableViewController :
+ UITableViewController
@end
// SelectColorTableViewController.m
@interface SelectColorTableViewController ()
-
@property (nonatomic) BOOL shouldCollapseDetailViewController;
-
@end
@implementation SelectColorTableViewController
@@ -153,33 +258,53 @@ class SelectColorTableViewController: UITableViewController, UISplitViewControll
- (void)viewDidLoad {
[super viewDidLoad];
- self.shouldCollapseDetailViewController = true;
+ self.shouldCollapseDetailViewController = YES;
self.splitViewController.delegate = self;
}
#pragma mark - UITableViewDelegate
-- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
- self.shouldCollapseDetailViewController = false;
+- (void)tableView:(UITableView *)tableView
+didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
+ self.shouldCollapseDetailViewController = NO;
}
#pragma mark - UISplitViewControllerDelegate
-- (BOOL)splitViewController:(UISplitViewController *)splitViewController collapseSecondaryViewController:(UIViewController *)secondaryViewController ontoPrimaryViewController:(UIViewController *)primaryViewController {
+- (BOOL)splitViewController:(UISplitViewController *)splitViewController
+collapseSecondaryViewController:(UIViewController *)secondaryViewController
+ ontoPrimaryViewController:(UIViewController *)primaryViewController {
return self.shouldCollapseDetailViewController;
}
@end
-~~~
+```
-When the user first opens up the app on iPhone 6+ in portrait orientation, `SelectColorViewController` gets displayed as the primary view controller. Once the user selects a color or the app goes into the background, the `SelectColorViewController` gets collapsed again, and the `ColorViewController` is displayed:
+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:
-
+
-* * *
-
-Be sure to check out the [`UISplitViewControllerDelegate`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UISplitViewControllerDelegate_protocol/index.html) documentation to learn about all the other fancy things you can do with the `UISplitViewController`.
-
-Given the new different device sizes we now have to work with as iOS developers, the UISplitViewController will soon be our new best friend!
+---
-> You can get the complete source code for the project used in this post [on GitHub](https://github.com/NatashaTheRobot/UISplitViewControllerDemo).
+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.
diff --git a/2014-11-10-core-location-in-ios-8.md b/2014-11-10-core-location-in-ios-8.md
index 701f3501..03323b72 100644
--- a/2014-11-10-core-location-in-ios-8.md
+++ b/2014-11-10-core-location-in-ios-8.md
@@ -70,7 +70,7 @@ func locationManager(manager: CLLocationManager!,
{
if status == .AuthorizedAlways || status == .AuthorizedWhenInUse {
manager.startUpdatingLocation()
- // ...
+ <#...#>
}
}
```
@@ -79,9 +79,9 @@ func locationManager(manager: CLLocationManager!,
Another change is required to use location services in iOS 8. In the past, one could optionally include a 'NSLocationUsageDescription' key in `Info.plist`. This value was a plain-text string explaining to the user for what the app was planning to use location services. This has since been split up into two separate keys (`NSLocationWhenInUseUsageDescription` and `NSLocationAlwaysUsageDescription`), and is now mandatory; if you call `requestWhenInUseAuthorization` or `requestAlwaysAuthorization` without the corresponding key, the prompt simply won't be shown to the user.
-
+
-
+
### Requesting Multiple Permissions
@@ -94,7 +94,7 @@ Here's an example of how an app that prompts for both kinds of permissions might
```swift
switch CLLocationManager.authorizationStatus() {
case .AuthorizedAlways:
- // ...
+ <#...#>
case .NotDetermined:
manager.requestAlwaysAuthorization()
case .AuthorizedWhenInUse, .Restricted, .Denied:
@@ -117,11 +117,11 @@ switch CLLocationManager.authorizationStatus() {
}
```
-
+
-
+
-
+
### Backwards Compatibility
@@ -169,7 +169,7 @@ class CLFloor : NSObject {
}
```
-```objective-c
+```objc
@interface CLFLoor : NSObject
@property(readonly, nonatomic) NSInteger level
@end
diff --git a/2014-11-17-uiprintinteractioncontroller.md b/2014-11-17-uiprintinteractioncontroller.md
index 2d220569..7a4315fc 100644
--- a/2014-11-17-uiprintinteractioncontroller.md
+++ b/2014-11-17-uiprintinteractioncontroller.md
@@ -17,11 +17,11 @@ UIKit makes it easy to print straight from a user's device with custom designs t
>
> As of Xcode 6, the printer simulator must be downloaded as part of the *[Hardware IO Tools for Xcode](https://developer.apple.com/downloads/index.action?name=hardware%20io%20tools)*.
-
+
-
+
-
+
* * *
@@ -91,7 +91,7 @@ Let's walk through a very simple case: showing the UI to print an image when the
}
```
-```objective-c
+```objc
- (IBAction)print:(id)sender {
if ([UIPrintInteractionController canPrintURL:self.imageURL]) {
UIPrintInfo *printInfo = [UIPrintInfo printInfo];
@@ -110,7 +110,7 @@ Let's walk through a very simple case: showing the UI to print an image when the
Easy as pie! _(Or, in this case, sautéed Swiss chard.)_
-
+
> The `presentAnimated(:completionHandler:)` method is for presenting the printing UI on the **iPhone**. If printing from the **iPad**, use one of the `presentFromBarButtonItem(:animated:completionHandler:)` or
`presentFromRect(:inView:animated:completionHandler:)` methods instead.
@@ -134,7 +134,7 @@ formatter.contentInsets = UIEdgeInsets(top: 72, left: 72, bottom: 72, right: 72)
printController.printFormatter = formatter
```
-```objective-c
+```objc
UIMarkupTextPrintFormatter *formatter = [[UIMarkupTextPrintFormatter alloc] initWithMarkupText:htmlString];
formatter.contentInsets = UIEdgeInsetsMake(72, 72, 72, 72); // 1" margins
@@ -143,21 +143,21 @@ printController.printFormatter = formatter;
The result? A handsomely rendered HTML page:
-
+
On the other hand, to use a `UIViewPrintFormatter`, you retrieve one from the view you want to print via its `viewPrintFormatter` property. Here's a look at how the formatter does its job for each of the three supported views:
#### 1) UITextView
-
+
#### 2) UIWebView
-
+
#### 3) MKMapView
-
+
## UIPrintPageRenderer
@@ -186,11 +186,11 @@ class RecipePrintPageRenderer: UIPrintPageRenderer {
addPrintFormatter(formatter, startingAtPageAtIndex: 0)
}
- // ...
+ <#...#>
}
```
-```objective-c
+```objc
@interface RecipePrintPageRenderer : UIPrintPageRenderer
@property (nonatomic, strong) NSString *authorName;
@property (nonatomic, strong) Recipe *recipe;
@@ -219,7 +219,7 @@ class RecipePrintPageRenderer: UIPrintPageRenderer {
return self;
}
-// ...
+<#...#>
@end
```
@@ -242,7 +242,7 @@ override func drawHeaderForPageAtIndex(pageIndex: Int, var inRect headerRect: CG
}
```
-```objective-c
+```objc
- (void)drawHeaderForPageAtIndex:(NSInteger)index
inRect:(CGRect)headerRect
{
@@ -272,7 +272,7 @@ override func drawContentForPageAtIndex(pageIndex: Int, inRect contentRect: CGRe
}
}
```
-```objective-c
+```objc
- (void)drawContentForPageAtIndex:(NSInteger)pageIndex
inRect:(CGRect)contentRect
{
@@ -294,7 +294,7 @@ let renderer = RecipePrintPageRenderer(authorName: "Nate Cook", recipe: selected
printController.printPageRenderer = renderer
```
-```objective-c
+```objc
RecipePrintPageRenderer *renderer = [[RecipePrintPageRenderer alloc] initWithAuthorName:@"Nate Cook" recipe:selectedRecipe];
printController.printPageRenderer = renderer;
```
@@ -303,7 +303,7 @@ The final result is much nicer than any of the built-in formatters.
> Note that the text of the recipe is being formatted by a `UIMarkupTextPrintFormatter`, while the header and images are drawn via custom code.
-
+
## Printing via a Share Sheet
@@ -320,7 +320,7 @@ With the tools we've learned above, adding printing capability in a share sheet
}
```
-```objective-c
+```objc
- (IBAction)openShareSheet:(id)sender {
UIPrintInfo *printInfo = ...
UISimpleTextPrintFormatter *formatter = ...
@@ -348,7 +348,7 @@ printerPicker.presentAnimated(true) {
}
```
-```objective-c
+```objc
UIPrinterPickerController *printPicker = [UIPrinterPickerController printerPickerControllerWithInitiallySelectedPrinter:self.savedPrinter];
[printPicker presentAnimated:YES completionHandler:
^(UIPrinterPickerController *printerPicker, BOOL userDidSelect, NSError *error) {
diff --git a/2014-12-01-watchkit.md b/2014-12-01-watchkit.md
index 08030b85..9f2b751d 100644
--- a/2014-12-01-watchkit.md
+++ b/2014-12-01-watchkit.md
@@ -1,7 +1,7 @@
---
title: WatchKit
-author: Mattt Thompson
-category: ""
+author: Mattt
+category: Miscellaneous
excerpt: "After taking a look at WatchKit, there were a few things that jumped out coming from UIKit. They're the kind of subjective, opinionated things that don't make for good documentation, but might be interesting or useful to anyone else as they're getting started."
status:
swift: 1.0
@@ -56,7 +56,7 @@ For sake of comparison, here is how one might understand WatchKit in terms of UI
| `WKInterfaceTable` | `UITableView` |
| `WKInterfaceTimer` | `UILabel` + `NSDateFormatter` + `NSTimer` |
-> As a [namespace prefix](http://nshipster.com/namespacing/), `WKInterface` sticks out like a sore thumb, but `WK` on its own was recently claimed by the new [WebKit](http://nshipster.com/wkwebkit/) framework. Although the Watch platform is a long way from being able to embed web views, the decision to demarcate things as they are is sensible.
+> As a [namespace prefix](https://nshipster.com/namespacing/), `WKInterface` sticks out like a sore thumb, but `WK` on its own was recently claimed by the new [WebKit](https://nshipster.com/wkwebkit/) framework. Although the Watch platform is a long way from being able to embed web views, the decision to demarcate things as they are is sensible.
There's a lot of overlap, but there are important differences. Understanding those differences is both informative to making great WatchKit apps as well as enlightening in terms of how Apple's thinking about API and best practices continues to evolve.
@@ -70,7 +70,7 @@ The designated initializer for `WKInterfaceController` is `initWithContext:`, wh
override init(context: AnyObject?) {
super.init(context: context)
- // ...
+ <#...#>
}
```
@@ -86,11 +86,11 @@ Overall, `WKInterfaceController`'s API has a tiny footprint, and there's no bett
// MARK: - WKInterfaceController
override func willActivate() {
- // ...
+ <#...#>
}
override func didDeactivate() {
- // ...
+ <#...#>
}
```
@@ -120,7 +120,7 @@ class WatchListsInterfaceController: WKInterfaceController, ListsControllerDeleg
}
}
- // ...
+ <#...#>
}
```
diff --git a/2014-12-08-apple-pay.md b/2014-12-08-apple-pay.md
index bd7de987..4b466d6b 100644
--- a/2014-12-08-apple-pay.md
+++ b/2014-12-08-apple-pay.md
@@ -1,7 +1,7 @@
---
title: Pay
author: Jack Flintermann
-category: ""
+category: Miscellaneous
excerpt: "There's a unique brand of modern angst that manifests the moment you decide to buy something online. While there's no English word for it, it translates roughly to \"Where is my credit card? What is its number? How badly do I actually want this thing, anyway?\""
status:
swift: 1.0
@@ -21,7 +21,7 @@ And if you're a developer accepting credit card payments in your app, this unfor
## Obtaining an Apple Merchant ID
-You'll have to register an Apple Merchant ID before testing anything out. Before doing this, you should choose a payment provider to actually handle your credit card processing. Apple provides a list of recommended ones at their [Apple Pay Developer Page](http://developer.apple.com/apple-pay) (disclosure: I work for [Stripe](https://stripe.com/), one of the recommended companies, but the code in this article doesn't depend on you choosing any specific provider). While your provider should have a specific guide for how to get set up using Apple Pay with their platform, the process looks like this:
+You'll have to register an Apple Merchant ID before testing anything out. Before doing this, you should choose a payment provider to actually handle your credit card processing. Apple provides a list of recommended ones at their [Apple Pay Developer Page](https://developer.apple.com/apple-pay) (disclosure: I work for [Stripe](https://stripe.com/), one of the recommended companies, but the code in this article doesn't depend on you choosing any specific provider). While your provider should have a specific guide for how to get set up using Apple Pay with their platform, the process looks like this:
- Head to the `Certificates, Identifiers, and Profiles` section of the Apple Developer center and [create a new merchant ID](https://developer.apple.com/account/ios/identifiers/merchant/merchantCreate.action).
- Next, [head to the Certificates section](https://developer.apple.com/account/ios/certificate/certificateCreate.action) and create a new Apple Pay Certificate. This involves uploading a Certificate Signing Request (CSR) to Apple. When you're signing up for a payment processor, they'll typically give you a CSR to use. You can use a CSR that you generate yourself to follow along with this guide, but your payment processor won't be able to decrypt payments made with it and you'll need to redo it later.
@@ -55,7 +55,7 @@ request.merchantCapabilities = .Capability3DS
Next, describe the things the customer is actually buying with the `paymentSummaryItems` property. This takes an array of `PKPaymentSummaryItem`s, which have a `label` and `amount`. They're analogous to line items on a receipt (which we'll see momentarily).
-
+
```swift
let wax = PKPaymentSummaryItem(label: "Mustache Wax", amount: NSDecimalNumber(string: "10.00"))
@@ -99,7 +99,7 @@ To understand how each of these components work, let's check out a timeline of h
- The `PKPaymentAuthorizationViewController` spinner animates into a success or failure icon. If successful, a notification will arrive from PassBook indicating a charge on the customer's credit card.
- Your delegate receives the `paymentAuthorizationViewControllerDidFinish:` callback. It is then responsible for calling `dismissViewControllerAnimated:completion` to dismiss the payment screen.
-
+
Concretely, this comes out looking like this:
@@ -181,7 +181,7 @@ Here, you should calculate the shipping rates for the selected address and then
I've set up a really simple web backend that queries the EasyPost API for shipping rates to a given address. The source is available at https://github.com/jflinter/example-shipping-api.
-Here's a function to query it, using [Alamofire](http://nshipster.com/alamofire/):
+Here's a function to query it, using [Alamofire](https://nshipster.com/alamofire/):
```swift
import AddressBook
@@ -251,7 +251,7 @@ func paymentAuthorizationViewController(controller: PKPaymentAuthorizationViewCo
}
```
-
+
Now, the customer can select an address and receive a different set of shipping options depending on where they live. Both the `shippingAddress` and `shippingMethod` they ultimately select will be available as properties on the `PKPayment` that is given to your delegate in the `paymentAuthorizationViewController:didAuthorizePayment:completion:` method.
diff --git a/2014-12-15-nsundomanager.md b/2014-12-15-nsundomanager.md
index 2ae1c0fd..5547bb74 100644
--- a/2014-12-15-nsundomanager.md
+++ b/2014-12-15-nsundomanager.md
@@ -41,7 +41,7 @@ func updateScore(score: NSNumber) {
}
```
-```objective-c
+```objc
- (void)updateScore:(NSNumber*)score {
[undoManager registerUndoWithTarget:self selector:@selector(updateScore:) object:myMovie.score];
[undoManager setActionName:NSLocalizedString(@"actions.update", @"Update Score")];
@@ -65,7 +65,7 @@ func movePiece(piece: ChessPiece, row:UInt, column:UInt) {
}
```
-```objective-c
+```objc
- (void)movePiece:(ChessPiece*)piece toRow:(NSUInteger)row column:(NSUInteger)column {
[[undoManager prepareWithInvocationTarget:self] movePiece:piece ToRow:piece.row column:piece.column];
[undoManager setActionName:NSLocalizedString(@"actions.move-piece", @"Move Piece")];
@@ -108,11 +108,11 @@ class ViewController: UIViewController {
return true
}
- // ...
+ <#...#>
}
```
-```objective-c
+```objc
@implementation ViewController
- (void)viewDidAppear:(BOOL)animated {
@@ -129,7 +129,7 @@ class ViewController: UIViewController {
return YES;
}
-// ...
+<#...#>
@end
```
@@ -164,7 +164,7 @@ func archiveEmail(email: Email) {
}
```
-```objective-c
+```objc
- (void)readAndArchiveEmail:(Email*)email {
[undoManager beginUndoGrouping];
[self markEmail:email asRead:YES];
@@ -214,7 +214,7 @@ func removeItem(item: NSObject) {
}
```
-```objective-c
+```objc
- (void)addItem:(id)item {
[undoManager registerUndoWithTarget:self selector:@selector(removeItem:) object:item];
if (![undoManager isUndoing]) {
@@ -236,6 +236,6 @@ If your test framework runs many tests as a part of one run loop (like Kiwi), cl
----
-There are even more ways to refine behavior with `NSUndoManager`, particularly for grouping actions and managing scope. Apple also provides [usability guidelines](https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/MobileHIG/UndoRedo.html) for making undo and redo accessible in an expected and delightful way.
+There are even more ways to refine behavior with `NSUndoManager`, particularly for grouping actions and managing scope. Apple also provides [usability guidelines](https://developer.apple.com/design/human-interface-guidelines/ios/user-interaction/undo-and-redo/) for making undo and redo accessible in an expected and delightful way.
We all may wish to live without mistakes, but Cocoa gives us a way to let our users live with fewer regrets as it makes some actions easily changeable.
diff --git a/2014-12-29-the-death-of-cocoa.md b/2014-12-29-the-death-of-cocoa.md
index 7d2c4b48..eb7ca09e 100644
--- a/2014-12-29-the-death-of-cocoa.md
+++ b/2014-12-29-the-death-of-cocoa.md
@@ -1,10 +1,11 @@
---
title: The Death of Cocoa
-author: Mattt Thompson
-category: ""
+author: Mattt
+category: Miscellaneous
excerpt: "For many of us, the simplicity, elegance, and performance of Apple's hardware and software working together are the reason why we build on their platforms. And yet, after just a few months of working with Swift, Cocoa has begun to lose its luster."
+retired: true
status:
- swift: 1.0
+ swift: 1.0
---
Cocoa is the de facto standard library of Objective-C, containing many of the essential frameworks for working in the language, such as Foundation, AppKit, and Core Data. Cocoa Touch is basically just Cocoa with UIKit substituted for AppKit, and is often used interchangeably with Cocoa to refer to the system frameworks on iOS.
@@ -21,7 +22,7 @@ What if we were to build a new Foundation from the Swift Standard Library? What
So to close out this historic year for Apple developers, let's take a moment to look forward at the possibilities going forward.
-* * *
+---
> If I have seen further it is by standing on the shoulders of giants.
> Isaac Newton
@@ -30,7 +31,7 @@ We owe all of our productivity to standard libraries.
When done well, standard libraries not only provide a common implementation of the most useful programming constructs, but they clarify those concepts in a transferable way. It's when a language's standard library diverges from existing (or even internal) convention that [things go south](http://eev.ee/blog/2012/04/09/php-a-fractal-of-bad-design/).
-For example, [`NSURLComponents`](http://nshipster.com/nsurl/) conforms to [RFC 3986](http://www.ietf.org/rfc/rfc3986)—a fact made explicit [in the documentation](https://developer.apple.com/library/prerelease/ios/documentation/Foundation/Reference/NSURLComponents_class/index.html). Not only do API consumers osmotically absorb the proper terminology and concepts as a byproduct of usage, but newcomers to the API that are already familiar with RFC 3986 can hit the ground running. (And how much easier it is to write documentation; just "RTFM" with a link to the spec!)
+For example, [`NSURLComponents`](https://nshipster.com/nsurl/) conforms to [RFC 3986](http://www.ietf.org/rfc/rfc3986)—a fact made explicit [in the documentation](https://developer.apple.com/library/prerelease/ios/documentation/Foundation/Reference/NSURLComponents_class/index.html). Not only do API consumers osmotically absorb the proper terminology and concepts as a byproduct of usage, but newcomers to the API that are already familiar with RFC 3986 can hit the ground running. (And how much easier it is to write documentation; just "RTFM" with a link to the spec!)
Standard libraries should implement standards.
@@ -42,23 +43,23 @@ Based on this assertion, let's take a look at some specific examples of what Coc
`NSNumber` exists purely as an object wrapper around integer, float, double, and boolean primitives. Without such concerns in Swift, there is no practical role for such a construct.
-Swift's standard library has done a remarkable job in structuring its numeric primitives, through a clever combination of [top-level functions and operators](http://nshipster.com/swift-default-protocol-implementations/) and type hierarchies. (And bonus points for including [literals for binary, octal, and hexadecimal in addition to decimal](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_487)). For lack of any real complaints about what's currently there, here are some suggestions for what might be added:
+Swift's standard library has done a remarkable job in structuring its numeric primitives, through a clever combination of [top-level functions and operators](https://nshipster.com/swift-default-protocol-implementations/) and type hierarchies. (And bonus points for including [literals for binary, octal, and hexadecimal in addition to decimal](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_487)). For lack of any real complaints about what's currently there, here are some suggestions for what might be added:
- A suitable replacement for `NSDecimalNumber`. Swift `Double`s are [documented as having 64-bit](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_484), while `NSDecimalNumber` can represent ["any number that can be expressed as `mantissa x 10^exponent` where mantissa is a decimal integer up to 38 digits long, and exponent is an integer from `–128` through `127`"](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSDecimalNumber_Class/index.html). In the meantime, [this gist](https://gist.github.com/mattt/1ed12090d7c89f36fd28) provides some of the necessary additions to work with `NSDecimalNumber` in Swift as one would any of the native numeric types.
- Complex number support, such as what's described in [this gist](https://gist.github.com/mattt/0576b9e4396ab5645aa9).
-- Simple native methods for [generating random numbers](http://nshipster.com/random/). [This gist](https://gist.github.com/mattt/f2ee2eed3570d1a9d644) has some examples of what that might look like.
+- Simple native methods for [generating random numbers](https://nshipster.com/random/). [This gist](https://gist.github.com/mattt/f2ee2eed3570d1a9d644) has some examples of what that might look like.
- Methods that take advantage of overloading to provide a uniform interface to performing calculations on one or many numbers, such as those in [Surge](https://github.com/mattt/surge).
- For Playgrounds, a framework with built-in mathematical notation, such as [Euler](https://github.com/mattt/euler), could make for a neat teaching tool.
### Strings
-The peril of strings is that they can encode so many different kinds of information. [As written previously](http://nshipster.com/nslocalizedstring/):
+The peril of strings is that they can encode so many different kinds of information. [As written previously](https://nshipster.com/nslocalizedstring/):
> Strings are perhaps the most versatile data type in computing. They're passed around as symbols, used to encode numeric values, associate values to keys, represent resource paths, store linguistic content, and format information.
`NSString` is perhaps _too_ versatile, though. Although it handles Unicode like a champ, the entire API is burdened by the conflation of strings as paths. `stringByAppendingPathComponent:` and its ilk are genuinely useful, but this usefulness ultimately stems from a misappropriation of strings as URLs.
-Much of this is due to the fact that `@"this"` (a string literal) is much more convenient than `[NSURL URLWithString:@"that"]` (a constructor). However, with [Swift's literal convertibles](http://nshipster.com/swift-literal-convertible/), it can be just as easy to build URL or Path values.
+Much of this is due to the fact that `@"this"` (a string literal) is much more convenient than `[NSURL URLWithString:@"that"]` (a constructor). However, with [Swift's literal convertibles](https://nshipster.com/swift-literal-convertible/), it can be just as easy to build URL or Path values.
One of the truly clever design choices for Swift's `String` is the internal use of encoding-independent Unicode characters, with exposed "views" to specific encodings:
@@ -66,7 +67,7 @@ One of the truly clever design choices for Swift's `String` is the internal use
> - A collection of UTF-16 code units (accessed with the string’s `utf16` property)
> - A collection of 21-bit Unicode scalar values, equivalent to the string’s UTF-32 encoding form (accessed with the string's `unicodeScalars` property)
-One of the only complaints of Swift `String`s are how much of its functionality is hampered by the way functionality is hidden in top-level functions. Most developers are trained to type `.` and wait for method completion for something like "count"; it's less obvious to consult the top-level `countElements` function. (Again, as described in the [Default Protocol Implementations article](http://nshipster.com/swift-default-protocol-implementations/), this could be solved if either Xcode or Swift itself allowed automatic bridging of explicit and implicit self in functions).
+One of the only complaints of Swift `String`s are how much of its functionality is hampered by the way functionality is hidden in top-level functions. Most developers are trained to type `.` and wait for method completion for something like "count"; it's less obvious to consult the top-level `countElements` function. (Again, as described in the [Default Protocol Implementations article](https://nshipster.com/swift-default-protocol-implementations/), this could be solved if either Xcode or Swift itself allowed automatic bridging of explicit and implicit self in functions).
### URI, URL, and URN
@@ -80,17 +81,17 @@ Building on these strong primitives, it is remarkably easy to create production-
It'd be amazing if the Swift standard library provided canonical implementations of a bunch of these (e.g. Tree, Singly- Doubly-Linked Lists, Queue / Stack). But I'll only make the case for one: Set.
-The three big collections in Foundation are `NSArray`, `NSDictionary`, and `NSSet` (and their mutable counterparts). Of these, `Set` is the only one currently missing. As a fundamental data structure, they are applicable to a wide variety of use cases. Specifically for Swift, though, `Set` could resolve one of the more awkward corners of the language—[RawOptionSetType](http://nshipster.com/rawoptionsettype/).
+The three big collections in Foundation are `NSArray`, `NSDictionary`, and `NSSet` (and their mutable counterparts). Of these, `Set` is the only one currently missing. As a fundamental data structure, they are applicable to a wide variety of use cases. Specifically for Swift, though, `Set` could resolve one of the more awkward corners of the language—[RawOptionSetType](https://nshipster.com/rawoptionsettype/).
-> For your consideration, [Nate Cook](http://nshipster.com/authors/nate-cook/) has built [a nice, complete implementation of `Set`](http://natecook.com/blog/2014/08/creating-a-set-type-in-swift/).
+> For your consideration, [Nate Cook](https://nshipster.com/authors/nate-cook/) has built [a nice, complete implementation of `Set`](http://natecook.com/blog/2014/08/creating-a-set-type-in-swift/).
### Dates & Times
The calendaring functionality is some of the oldest and most robust in Cocoa. Whereas with most other languages, date and time programming is cause for fear, one does not get the same sense of dread when working with `NSDate` and `NSCalendar`. However, it suffers from being difficult to use and impossible to extend.
-In order to do any calendaring calculations, such as getting the date one month from today, one would use `NSCalendar` and [`NSDateComponents`](http://nshipster.com/nsdatecomponents/). That's the _correct_ way to do it, at least... a majority of developers probably still use `dateWithTimeIntervalSinceNow:` with a constant number of seconds hardcoded. Tragically, it's not enough for an API to do things the right way, it must also be easier than doing it the wrong way.
+In order to do any calendaring calculations, such as getting the date one month from today, one would use `NSCalendar` and [`NSDateComponents`](https://nshipster.com/nsdatecomponents/). That's the _correct_ way to do it, at least... a majority of developers probably still use `dateWithTimeIntervalSinceNow:` with a constant number of seconds hardcoded. Tragically, it's not enough for an API to do things the right way, it must also be easier than doing it the wrong way.
-Another shortfall (albeit incredibly minor) of `NSCalendar` is that it doesn't allow for new calendars to be added. For someone doing their darnedest to advocate conversion to the [French Republican Calendar](http://en.wikipedia.org/wiki/French_Republican_Calendar), this is bothersome.
+Another shortfall (albeit incredibly minor) of `NSCalendar` is that it doesn't allow for new calendars to be added. For someone doing their darnedest to advocate conversion to the [French Republican Calendar](https://en.wikipedia.org/wiki/French_Republican_Calendar), this is bothersome.
Fortunately, all of the new language features of Swift could be used to solve both of these problems in a really elegant way. It'd take some work to implement, but a calendaring system based on generics could really be something. If anyone wants to take me up on that challenge, [here are some ideas](https://gist.github.com/mattt/7edb54f8f4fde4a3783e).
@@ -111,7 +112,7 @@ if let JSON = NSJSONSerialization.JSONObjectWithData(data, options: nil, error:
}
```
-```objective-c
+```objc
NSData *data;
NSError *error = nil;
id JSON = [NSJSONSerialization JSONObjectWithData:data
@@ -122,15 +123,16 @@ if (!error) {
NSString *name = product[@"name"];
NSNumber *price = product[@"price"];
- // ...
+ <#...#>
}
}
```
> In defense of Apple, I once asked an engineer at a WWDC Lab why it took so long for iOS to support JSON. Their answer made a lot of sense. Paraphrasing:
->> Apple is a company with a long view of technology. It's _really_ difficult to tell whether a technology like JSON is going to stick, or if it's just another fad. Apple once released a framework for [PubSub](https://developer.apple.com/library/mac/documentation/InternetWeb/Reference/PubSubReference/_index.html), which despite not being widely known or used, still has to be supported for the foreseeable future. Each technology is a gamble of engineering resources.
+>
+> > Apple is a company with a long view of technology. It's _really_ difficult to tell whether a technology like JSON is going to stick, or if it's just another fad. Apple once released a framework for [PubSub](https://developer.apple.com/library/mac/documentation/InternetWeb/Reference/PubSubReference/_index.html), which despite not being widely known or used, still has to be supported for the foreseeable future. Each technology is a gamble of engineering resources.
-Data marshaling and serialization are boring tasks, and boring tasks are exactly what a standard library should take care of. Apple knew this when developing Cocoa, which has robust implementations for both text and binary [property lists](http://en.wikipedia.org/wiki/Property_list), which are the lifeblood of iOS and OS X. It may be difficult to anticipate what other interchange formats will be viable in the long term, but providing official support for emerging technologies on a probationary basis would do a lot to improve things for developers.
+Data marshaling and serialization are boring tasks, and boring tasks are exactly what a standard library should take care of. Apple knew this when developing Cocoa, which has robust implementations for both text and binary [property lists](https://en.wikipedia.org/wiki/Property_list), which are the lifeblood of iOS and OS X. It may be difficult to anticipate what other interchange formats will be viable in the long term, but providing official support for emerging technologies on a probationary basis would do a lot to improve things for developers.
### Regular Expressions
@@ -138,9 +140,9 @@ Regexes are a staple of scripting languages—enough so that they often have a d
### Errors
-Objective-C is rather exceptional in [how it uses error pointers](http://nshipster.com/nserror/) (`NSError **`) to communicate runtime failures rather than `@throw`-ing exceptions. It's a pattern every Cocoa developer should be familiar with:
+Objective-C is rather exceptional in [how it uses error pointers](https://nshipster.com/nserror/) (`NSError **`) to communicate runtime failures rather than `@throw`-ing exceptions. It's a pattern every Cocoa developer should be familiar with:
-```objective-c
+```objc
NSError *error = nil;
BOOL success = [[NSFileManager defaultManager] moveItemAtPath:@"/path/to/target"
toPath:@"/path/to/destination"
@@ -186,31 +188,31 @@ Patterns like this have emerged from a community eager to improve on existing pa
AppKit and UIKit are entire topics unto themselves. It's much more likely that the two would take further steps to unify than be rewritten or adapted to Swift anytime soon. A much more interesting question is whether Swift will expand beyond the purview of iOS & OS X development, such as for systems or web scripting, and how that would fundamentally change the role of Cocoa as a de facto standard library.
-* * *
+---
## Thinking Further
Perhaps we're thinking too small about what a standard library can be.
-The Wolfram Language has [The Mother of All Demos](https://www.youtube.com/watch?v=_P9HqHVPeik#t=1m02.5s) ([with apologies to Douglas Engelbart](http://en.wikipedia.org/wiki/The_Mother_of_All_Demos)) for a programming language.
+The Wolfram Language has [The Mother of All Demos](https://www.youtube.com/watch?v=_P9HqHVPeik#t=1m02.5s) ([with apologies to Douglas Engelbart](https://en.wikipedia.org/wiki/The_Mother_of_All_Demos)) for a programming language.
> Granted, Wolfram is a parallel universe of computation where nothing else exists, and the language itself is a hot mess.
Here's an overview of the [functionality offered in its standard library](http://reference.wolfram.com/language/):
-| | | | |
-|----------------------------|-------------------------|--------------------------------|--------------------------|
-| 2D / 3D Visualization | Graph Analysis | Data Analytics | Image Processing |
-| Audio Processing | Machine Learning | Equation Solving | Algebraic Computation |
-| Arbitrary Precision | Calculus Computation | Matrix Computation | String Manipulation |
-| Combinatorial Optimization | Computational Geometry | Database Connectivity | Built-In Testing |
-| Device Connectivity | Functional Programming | Natural Language Understanding | Sequence Analysis |
-| Time Series | Geographic Data | Geomapping | Weather Data |
-| Physics & Chemistry Data | Genomic Data | Units & Measures | Control Theory |
-| Reliability Analysis | Parallel Computation | Engineering Data | Financial Data |
-| Financial Computation | Socioeconomic Data | Popular Culture Data | Boolean Computation |
-| Number Theory | Document Generation | Table Formatting | Mathematical Typesetting |
-| Interactive Controls | Interface Building | Form Construction | XML Templating |
+| | | | |
+| -------------------------- | ---------------------- | ------------------------------ | ------------------------ |
+| 2D / 3D Visualization | Graph Analysis | Data Analytics | Image Processing |
+| Audio Processing | Machine Learning | Equation Solving | Algebraic Computation |
+| Arbitrary Precision | Calculus Computation | Matrix Computation | String Manipulation |
+| Combinatorial Optimization | Computational Geometry | Database Connectivity | Built-In Testing |
+| Device Connectivity | Functional Programming | Natural Language Understanding | Sequence Analysis |
+| Time Series | Geographic Data | Geomapping | Weather Data |
+| Physics & Chemistry Data | Genomic Data | Units & Measures | Control Theory |
+| Reliability Analysis | Parallel Computation | Engineering Data | Financial Data |
+| Financial Computation | Socioeconomic Data | Popular Culture Data | Boolean Computation |
+| Number Theory | Document Generation | Table Formatting | Mathematical Typesetting |
+| Interactive Controls | Interface Building | Form Construction | XML Templating |
Conventional wisdom would suggest that, yes: it is unreasonable for a standard library to encode [the production budget of the movie _Avatar_](http://reference.wolfram.com/language/ref/MovieData.html#Examples), [the max speed of a McDonnell Douglas F/A-18 Hornet](http://reference.wolfram.com/language/ref/AircraftData.html#Example), or [the shape of France](http://reference.wolfram.com/language/ref/CountryData.html#Example). That is information that can be retrieved by querying IMDB, scraping Wikipedia, and importing from a GIS system.
@@ -218,13 +220,13 @@ But other things, like [converting miles to kilometers](http://reference.wolfram
Indeed, what sets Cocoa apart from most other standard libraries is all of the specific information it encodes in `NSLocale` and `NSCalendar`, but most of this comes from the [Unicode Common Locale Data Repository (CLDR)](http://cldr.unicode.org).
-What's to stop a standard library from pulling in other data sources? Why not expose an interface to [libphonenumber](https://github.com/googlei18n/libphonenumber), or expand on what [HealthKit is already doing](http://nshipster.com/nsformatter/#mass,-length,-&-energy-formatters) for fundamental units?
+What's to stop a standard library from pulling in other data sources? Why not expose an interface to [libphonenumber](https://github.com/googlei18n/libphonenumber), or expand on what [HealthKit is already doing](https://nshipster.com/nsformatter/#mass,-length,-&-energy-formatters) for fundamental units?
Incorporating this kind of data in an organized, meaningful way is too much to expect for a third-party framework, and too important to delegate to the free market of open source.
> Yes, in many ways, the question of the role of a standard library is the same as the question of what roles the public and private sectors have in society. Third-Party Libertarians, meet Third-Party Librarytarians.
-* * *
+---
Swift is compelling not just in terms of what the language itself can do, but what it means to Apple, to iOS & OS X developers, and the developer community at large. There are so many factors in play that questions of technical feasibility cannot be extricated from their social and economic consequences.
@@ -235,4 +237,3 @@ What is almost certain, however, is that Cocoa, like Objective-C, is doomed. It'
**The Swift Standard Library is on a collision course with Cocoa, and if the new language continues to gain momentum, one should expect to see further fracturing and reinvention within the system frameworks.**
For 30 years, these technologies have served us well, and the best we can do to honor their contributions is to learn from their mistakes and make sure that what replaces them are insanely great.
-
diff --git a/2015-01-01-new-years-2015.md b/2015-01-01-new-years-2015.md
index fa65cc93..e47c45f4 100644
--- a/2015-01-01-new-years-2015.md
+++ b/2015-01-01-new-years-2015.md
@@ -1,6 +1,6 @@
---
title: "Reader Submissions - New Year's 2015"
-author: Mattt Thompson
+author: Mattt
category: Reader Submissions
excerpt: "2014 was an incredible year professionally for Apple developers. With a deluge of new developments—both from Cupertino and the community at large—there were no shortage of interesting tidbits for readers to share."
status:
@@ -37,7 +37,7 @@ From [Samuel Defago](https://github.com/defagos):
> Given the fact that literals are most of the time associated with numbers and collections, I often forget that they work for UTF8-encoded `NULL`-terminated C-strings as well, especially when I write code using the runtime:
-```objective-c
+```objc
NSString *propertyAttributesString =
@(property_getAttributes(class_getProperty([NSObject class], "description")));
// T@"NSString",R,C
@@ -47,7 +47,7 @@ NSString *propertyAttributesString =
[Nolan O'Brien](https://github.com/NSProgrammer) brings the `AmIBeingDebugged` function to our attention from from [this Technical Q&A document](https://developer.apple.com/library/mac/qa/qa1361/_index.html):
-```objective-c
+```objc
#include
#include
#include
@@ -90,7 +90,7 @@ From [Vadim Shpakovski](https://github.com/shpakovski):
> Here is a convenient way to access child controllers inserted into Storyboard container views:
-```objective-c
+```objc
// 1. A property has the same name as a segue identifier in XIB
@property (nonatomic) ChildViewController1 *childController1;
@property (nonatomic) ChildViewController2 *childController2;
@@ -154,7 +154,7 @@ imageView.image = UIImage(contentsOfFile: pathToFileInSharedSubfolder("image.png
* * *
-The rest of this year's reader submissions come from [Cédric Luthi](https://github.com/0xced), who (as in [years](http://nshipster.com/new-years-2014/) [past](http://nshipster.com/reader-submissions-new-years-2013/)) contributed a payload of tips and tricks worthy of an entire article unto themselves. Thanks so much for these, Cédric!
+The rest of this year's reader submissions come from [Cédric Luthi](https://github.com/0xced), who (as in [years](https://nshipster.com/new-years-2014/) [past](https://nshipster.com/reader-submissions-new-years-2013/)) contributed a payload of tips and tricks worthy of an entire article unto themselves. Thanks so much for these, Cédric!
## CocoaPods, Exposed!
@@ -208,7 +208,7 @@ From a comment in `NSBundle.h`:
>
> Here is an excerpt of the MediaPlayer framework class-dump on iOS 6.1:
-```objective-c
+```objc
@interface MPMoviePlayerController : NSObject
{
void *_internal; // 4 = 0x4
@@ -218,7 +218,7 @@ From a comment in `NSBundle.h`:
> Since `id internal = [moviePlayerController valueForKey:@"internal"]` doesn't work, here is the hardcore way to access the internal ivar:
-```objective-c
+```objc
id internal = *((const id*)(void*)((uintptr_t)moviePlayerController + sizeof(Class)));
```
@@ -230,7 +230,7 @@ id internal = *((const id*)(void*)((uintptr_t)moviePlayerController + sizeof(Cla
>
> From the [documentation](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSDateFormatter_Class/#//apple_ref/occ/clm/NSDateFormatter/dateFormatFromTemplate:options:locale):
-```objective-c
+```objc
+ (NSString *)dateFormatFromTemplate:(NSString *)template
options:(NSUInteger)opts
locale:(NSLocale *)locale
@@ -240,7 +240,7 @@ id internal = *((const id*)(void*)((uintptr_t)moviePlayerController + sizeof(Cla
>>
>> The following example shows the difference between the date formats for British and American English:
-```objective-c
+```objc
NSLocale *usLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
NSLocale *gbLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_GB"];
@@ -292,7 +292,7 @@ NSLog(@"Date format for %@: %@",
> Unfortunately, the associated objects `OBJC_ASSOCIATION_ASSIGN` policy does not support zeroing weak references. Fortunately, it's quite easy to implement yourself. You just need a simple class to wrap an object with a weak reference:
-```objective-c
+```objc
@interface WeakObjectContainter : NSObject
@property (nonatomic, readonly, weak) id object;
@end
@@ -313,13 +313,13 @@ NSLog(@"Date format for %@: %@",
> Then, associate the `WeakObjectContainter` with `OBJC_ASSOCIATION_RETAIN(_NONATOMIC):`
-```objective-c
+```objc
objc_setAssociatedObject(self, &MyKey, [[WeakObjectContainter alloc] initWithObject:object], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
```
> Use the `object` property to access it in order to get a zeroing weak reference to the desired object:
-```objective-c
+```objc
id object = [objc_getAssociatedObject(self, &MyKey) object];
```
diff --git a/2015-01-05-changing-of-the-guard.md b/2015-01-05-changing-of-the-guard.md
index f83ac3a3..3a75953c 100644
--- a/2015-01-05-changing-of-the-guard.md
+++ b/2015-01-05-changing-of-the-guard.md
@@ -1,13 +1,14 @@
---
title: "Changing of the Guard"
-author: Mattt Thompson
-category: ""
+author: Mattt
+category: Miscellaneous
excerpt: "An important site announcement."
+published: false
status:
- swift: n/a
+ swift: n/a
---
-I started NSHipster in the summer of 2012. What began as a snarky writing exercise to capture observations from my daily interactions with Cocoa & Objective-C became a full-time passion for learning as much as I could about my craft.
+I started NSHipster in the summer of 2012. What began as a snarky writing exercise to capture observations from my daily interactions with Cocoa & Objective-C became a full-time passion for learning as much as I could about my craft.
As of today, I am stepping down from my role as managing editor of NSHipster. Nate Cook ([@nnnnnnnn](https://twitter.com/nnnnnnnn)) will be taking over ownership and editorial oversight for the site. Nate is a talented engineer and writer who embodies the spirit of curiosity that characterizes NSHipster and its readers. I'm fully confident that he'll continue to create and cultivate the great content you've come to expect every week.
diff --git a/2015-01-12-long-live-cocoa.md b/2015-01-12-long-live-cocoa.md
index 531fbe3e..c30b4e61 100644
--- a/2015-01-12-long-live-cocoa.md
+++ b/2015-01-12-long-live-cocoa.md
@@ -1,22 +1,20 @@
---
title: "Long Live Cocoa"
author: Nate Cook
-category: ""
+category: Miscellaneous
excerpt: "Swift is an exciting language for many of us, but it's still brand new. The stability of Objective-C and the history and strength of Cocoa mean that Swift isn't ready to be the driving force behind a major change, at least not quite yet. Cocoa's depth and the power it affords, along with the way it and Swift go hand in hand, make Cocoa as relevant and as promising as ever."
-hiddenlang: ""
+retired: true
status:
- swift: 1.0
+ swift: 1.0
---
It's the start of a new year—2015, the year of Watch, the first full year for Swift, and a bit of a new start for NSHipster. Before we get caught up in the excitement of new devices and the next beta of Xcode or start planning our trips to WWDC 2015, let's take a moment to look at our tools as they are today: Objective-C, Swift, and most importantly, Cocoa.
Swift is an exciting language for many of us, but it's still brand new. The stability of Objective-C and the history and strength of Cocoa mean that Swift isn't ready to be the driving force behind a [major change](/the-death-of-cocoa/), at least not quite yet. Cocoa's depth and the power it affords, along with the way it and Swift go hand in hand, make Cocoa as relevant and as promising as ever. In fact, I don't think there's been a more exciting time to be a Cocoa developer.
+---
-* * *
-
-
-Cocoa is an impressively deep API—dig a little below the surface of any common tool and you unearth a trove of functionality. You need look no further for proof than the incredible work [Mattt](http://nshipster.com/authors/mattt-thompson/) has done in these very pages over the last few years, illuminating what we didn't know Cocoa could do. To name just a few:
+Cocoa is an impressively deep API—dig a little below the surface of any common tool and you unearth a trove of functionality. You need look no further for proof than the incredible work [Mattt](https://nshipster.com/authors/mattt-thompson/) has done in these very pages over the last few years, illuminating what we didn't know Cocoa could do. To name just a few:
- The foundations of natural language interfaces with [`NSLinguisticTagger`](/nslinguistictagger/) and [`AVSpeechSynthesizer`](/avspeechsynthesizer/)
- Simple data persistence with [`NSCoding` and `NSKeyedArchiver`](/nscoding/)
@@ -28,57 +26,53 @@ Cocoa is an impressively deep API—dig a little below the surface of any common
The list goes on and on. (Check it out—right there on the [front page](/#archive).)
-
### Hand in Hand
What's more, Cocoa and Swift are practically—and in Swift's case, literally—made for each other.
On the Cocoa side, changes to the toolset over the past few years paved the way for Cocoa to be Swift-friendly right out of the gate. Shifting to LLVM/Clang, adding block syntax to Objective-C, pushing the `NS_ENUM` & `NS_OPTIONS` macros, converting initializers to return `instancetype`—all these steps make the Cocoa APIs we're using today far more compatible with Swift than they could have been even a few years ago. Whenever you supply a Swift closure as a `NSURLSession` completion handler, or use the suggested completions for `UIModalTransitionStyle`, you're building on that work, done years ago when Swift was still behind closed doors (or in Chris Lattner's head).
-Swift was then designed from the ground up to be used with Cocoa. If I could nominate a single Swift feature as [most confusing to newcomers](http://stackoverflow.com/search?q=swift+unwrapped+unexpectedly), it would be Optionals, with their extra punctuation and unwrapping requirements. Even so, Optionals represent a crowning achievement, one so foundational it fades into the woodwork: Swift is a brand-new language that *doesn't* require a brand-new API. It's a type-safe, memory-safe language whose primary purpose is interacting directly with the enormous C-based Cocoa API, with pointers and raw memory lying all over the place.
+Swift was then designed from the ground up to be used with Cocoa. If I could nominate a single Swift feature as [most confusing to newcomers](http://stackoverflow.com/search?q=swift+unwrapped+unexpectedly), it would be Optionals, with their extra punctuation and unwrapping requirements. Even so, Optionals represent a crowning achievement, one so foundational it fades into the woodwork: Swift is a brand-new language that _doesn't_ require a brand-new API. It's a type-safe, memory-safe language whose primary purpose is interacting directly with the enormous C-based Cocoa API, with pointers and raw memory lying all over the place.
This is no small feat. The developer tools team at Apple has been busy annotating the entire API with information about memory management for parameters and return values. Once annotated, functions can be used safely from within Swift, since the compiler knows how to bridge types back and forth from Swift to annotated C code.
Here's an example of similar annotated and unannotated functions. First, the C versions:
-````c
+```c
// Creates an immutable copy of a string.
CFStringRef CFStringCreateCopy ( CFAllocatorRef alloc, CFStringRef theString );
// Encodes an OSType into a string suitable for use as a tag argument.
CFStringRef UTCreateStringForOSType ( OSType inOSType );
-````
+```
-Both of these functions return a `CFStringRef`—a reference to a `CFString`. A `CFStringRef` can be bridged to a Swift `CFString` instance, but this is *only* safe if the method has been annotated. In Swift, you can readily see the difference:
+Both of these functions return a `CFStringRef`—a reference to a `CFString`. A `CFStringRef` can be bridged to a Swift `CFString` instance, but this is _only_ safe if the method has been annotated. In Swift, you can readily see the difference:
-````swift
+```swift
// annotated: returns a memory-managed Swift `CFString`
func CFStringCreateCopy(alloc: CFAllocator!, theString: CFString!) -> CFString!
// unannotated: returns an *unmanaged* `CFString`
func UTCreateStringForOSType(inOSType: OSType) -> Unmanaged!
-````
+```
Upon receiving an `Unmanaged!`, you need to follow up with `.takeRetainedValue()` or `.takeUnretainedValue()` to get a memory-managed `CFString` instance. Which to call? To know that, you have to read the documentation or know the conventions governing whether the result you get back is retained or unretained. By annotating these functions, Apple has done that work for you, already guaranteeing memory safety across a huge swath of Cocoa.
-
-* * *
-
+---
Moreover, Swift doesn't just embrace Cocoa APIs, it actively improves them. Take the venerable `CGRect`, for example. As a C struct, it can't contain any instance methods, so all the [tools to manipulate `CGRect`s](/cggeometry/) live in top-level functions. These tools are powerful, but you need to know they exist and how to put them to use. These four lines of code, dividing a `CGRect` into two smaller pieces, might require three trips to the documentation:
-````objective-c
+```objc
CGRect nextRect;
CGRect remainingRect;
CGRectDivide(sourceRect, &nextRect, &remainingRect, 250, CGRectMinXEdge);
NSLog("Remaining rect: %@", NSStringFromCGRect(remainingRect));
-````
+```
In Swift, structs happily contain both static and instance methods and computed properties, so Core Graphics extends `CGRect` to make finding and using those tools far easier. Because `CGRect*` functions are mapped to instance methods or properties, the code above is reduced to this:
-````swift
+```swift
let (nextRect, remainingRect) = sourceRect.rectsByDividing(250, CGRectEdge.MinXEdge)
println("Remaining rect: \(remainingRect)")
-````
-
+```
### Getting Better All The Time
@@ -86,7 +80,7 @@ To be sure, working with Cocoa and Swift together is sometimes awkward. Where th
For example, `NSTimer` has a perfectly fine interface, but it suffers from requiring an Objective-C method to call, either via target-selector or invocation. When defining a timer, chances are I already have everything ready to go. [With a simple `NSTimer` extension](https://gist.github.com/natecook1000/b0285b518576b22c4dc8) using its toll-free bridged Core Foundation counterpart, `CFTimer`, we're in business in no time:
-````swift
+```swift
let message = "Are we there yet?"
let alert = UIAlertController(title: message, message: nil, preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "No", style: .Default, handler: nil))
@@ -97,11 +91,8 @@ NSTimer.scheduledTimerWithTimeInterval(10, repeats: true) { [weak self] timer in
}
}
// I swear I'll turn this car around.
-````
-
+```
-* * *
-
-
-None of this is to refute [Mattt's last post](/the-death-of-cocoa/), though—on an infinite time scale, we'll surely be coding against Cocoa's successor on our 42" iPads while looking out across the Titan moonscape. But as long as Cocoa's still around, isn't it *great?*
+---
+None of this is to refute [Mattt's last post](/the-death-of-cocoa/), though—on an infinite time scale, we'll surely be coding against Cocoa's successor on our 42" iPads while looking out across the Titan moonscape. But as long as Cocoa's still around, isn't it _great?_
diff --git a/2015-01-19-javascriptcore.md b/2015-01-19-javascriptcore.md
deleted file mode 100644
index 2557736d..00000000
--- a/2015-01-19-javascriptcore.md
+++ /dev/null
@@ -1,357 +0,0 @@
----
-title: "JavaScriptCore"
-author: Nate Cook
-category: "Cocoa"
-excerpt: "Introduced with OS X Mavericks and iOS 7, the JavaScriptCore framework puts an Objective-C wrapper around WebKit's JavaScript engine, providing easy, fast, and safe access to the world's most prevalent language. Love it or hate it, JavaScript's ubiquity has led to an explosion of developers, tools, and resources along with ultra-fast virtual machines like the one built into OS X and iOS."
-status:
- swift: 2.0
- reviewed: November 9, 2015
----
-
-An updated ranking of programming language popularity is [out this week](http://redmonk.com/sogrady/category/programming-languages/), showing Swift leaping upward through the ranks from 68th to 22nd, while Objective-C holds a strong lead up ahead at #10. Both, however, are blown away by the only other language allowed to run natively on iOS: the current champion, JavaScript.
-
-Introduced with OS X Mavericks and iOS 7, the JavaScriptCore framework puts an Objective-C wrapper around WebKit's JavaScript engine, providing easy, fast, and safe access to the world's most prevalent language. Love it or hate it, JavaScript's ubiquity has led to an explosion of developers, tools, and resources along with ultra-fast virtual machines like the one built into OS X and iOS.
-
-So come, lay aside bitter debates about dynamism and type safety, and join me for a tour of *JavaScriptCore.*
-
-
-* * *
-
-### `JSContext` / `JSValue`
-
-`JSContext` is an environment for running JavaScript code. A `JSContext` instance represents the global object in the environment—if you've written JavaScript that runs in a browser, `JSContext` is analogous to `window`. After creating a `JSContext`, it's easy to run JavaScript code that creates variables, does calculations, or even defines functions:
-
-````swift
-let context = JSContext()
-context.evaluateScript("var num = 5 + 5")
-context.evaluateScript("var names = ['Grace', 'Ada', 'Margaret']")
-context.evaluateScript("var triple = function(value) { return value * 3 }")
-let tripleNum: JSValue = context.evaluateScript("triple(num)")
-````
-````objective-c
-JSContext *context = [[JSContext alloc] init];
-[context evaluateScript:@"var num = 5 + 5"];
-[context evaluateScript:@"var names = ['Grace', 'Ada', 'Margaret']"];
-[context evaluateScript:@"var triple = function(value) { return value * 3 }"];
-JSValue *tripleNum = [context evaluateScript:@"triple(num)"];
-````
-
-As that last line shows, any value that comes *out* of a `JSContext` is wrapped in a `JSValue` object. A language as dynamic as JavaScript requires a dynamic type, so `JSValue` wraps every possible kind of JavaScript value: strings and numbers; arrays, objects, and functions; even errors and the special JavaScript values `null` and `undefined`.
-
-`JSValue` includes a host of methods for accessing its underlying value as the correct Foundation type, including:
-
-| JavaScript Type | `JSValue` method | Objective-C Type | Swift Type
-|-----------------|------------------------------------|---------------------|----------------------------------|
-| string | `toString` | `NSString` | `String!`
-| boolean | `toBool` | `BOOL` | `Bool`
-| number | `toNumber` `toDouble` `toInt32` `toUInt32` | `NSNumber` `double` `int32_t` `uint32_t` | `NSNumber!` `Double` `Int32` `UInt32`
-| Date | `toDate` | `NSDate` | `NSDate!`
-| Array | `toArray` | `NSArray` | `[AnyObject]!`
-| Object | `toDictionary` | `NSDictionary` | `[NSObject : AnyObject]!`
-| Object | `toObject` `toObjectOfClass:` | *custom type* | *custom type*
-
-To retrieve the value of `tripleNum` from the above example, simply use the appropriate method:
-
-````swift
-print("Tripled: \(tripleNum.toInt32())")
-// Tripled: 30
-````
-````objective-c
-NSLog(@"Tripled: %d", [tripleNum toInt32]);
-// Tripled: 30
-````
-
-
-### Subscripting Values
-
-We can easily access any values we've created in our `context` using subscript notation on both `JSContext` and `JSValue` instances. `JSContext` requires a string subscript, while `JSValue` allows either string or integer subscripts for delving down into objects and arrays:
-
-````swift
-let names = context.objectForKeyedSubscript("names")
-let initialName = names.objectAtIndexedSubscript(0)
-print("The first name: \(initialName.toString())")
-// The first name: Grace
-````
-````objective-c
-JSValue *names = context[@"names"];
-JSValue *initialName = names[0];
-NSLog(@"The first name: %@", [initialName toString]);
-// The first name: Grace
-````
-
-> Swift shows its youth, here—while Objective-C code can take advantage of subscript notation, Swift currently only exposes the [raw methods](/object-subscripting/) that should make such subscripting possible: `objectForKeyedSubscript()` and `objectAtIndexedSubscript()`.
-
-
-### Calling Functions
-
-With a `JSValue` that wraps a JavaScript function, we can call that function directly from our Objective-C/Swift code using Foundation types as parameters. Once again, JavaScriptCore handles the bridging without any trouble:
-
-````swift
-let tripleFunction = context.objectForKeyedSubscript("triple")
-let result = tripleFunction.callWithArguments([5])
-print("Five tripled: \(result.toInt32())")
-````
-````objective-c
-JSValue *tripleFunction = context[@"triple"];
-JSValue *result = [tripleFunction callWithArguments:@[@5] ];
-NSLog(@"Five tripled: %d", [result toInt32]);
-````
-
-
-### Exception Handling
-
-`JSContext` has another useful trick up its sleeve: by setting the context's `exceptionHandler` property, you can observe and log syntax, type, and runtime errors as they happen. `exceptionHandler` is a callback handler that receives a reference to the `JSContext` and the exception itself:
-
-````swift
-context.exceptionHandler = { context, exception in
- print("JS Error: \(exception)")
-}
-
-context.evaluateScript("function multiply(value1, value2) { return value1 * value2 ")
-// JS Error: SyntaxError: Unexpected end of script
-````
-````objective-c
-context.exceptionHandler = ^(JSContext *context, JSValue *exception) {
- NSLog(@"JS Error: %@", exception);
-};
-
-[context evaluateScript:@"function multiply(value1, value2) { return value1 * value2 "];
-// JS Error: SyntaxError: Unexpected end of script
-````
-
-
-
-## JavaScript Calling
-
-Now we know how to extract values from a JavaScript environment and call functions defined therein. What about the reverse? How can we get access to our custom objects and methods, defined in Objective-C or Swift, from within the JavaScript realm?
-
-There are two main ways of giving a `JSContext` access to our native client code: blocks and the `JSExport` protocol.
-
-### Blocks
-
-When an Objective-C block is assigned to an identifier in a `JSContext`, JavaScriptCore automatically wraps the block in a JavaScript function. This makes it simple to use Foundation and Cocoa classes from within JavaScript—again, all the bridging happens for you. Witness the full power of Foundation string transformations, now accessible to JavaScript:
-
-````swift
-let simplifyString: @convention(block) String -> String = { input in
- let result = input.stringByApplyingTransform(NSStringTransformToLatin, reverse: false)
- return result?.stringByApplyingTransform(NSStringTransformStripCombiningMarks, reverse: false) ?? ""
-}
-context.setObject(unsafeBitCast(simplifyString, AnyObject.self), forKeyedSubscript: "simplifyString")
-
-print(context.evaluateScript("simplifyString('안녕하새요!')"))
-// annyeonghasaeyo!
-````
-````objective-c
-context[@"simplifyString"] = ^(NSString *input) {
- NSMutableString *mutableString = [input mutableCopy];
- CFStringTransform((__bridge CFMutableStringRef)mutableString, NULL, kCFStringTransformToLatin, NO);
- CFStringTransform((__bridge CFMutableStringRef)mutableString, NULL, kCFStringTransformStripCombiningMarks, NO);
- return mutableString;
-};
-
-NSLog(@"%@", [context evaluateScript:@"simplifyString('안녕하세요!')"]);
-````
-
-> There's another speedbump for Swift here—note that this only works for *Objective-C blocks*, not Swift closures. To use a Swift closure in a `JSContext`, it needs to be (a) declared with the `@convention(block)` attribute, and (b) cast to `AnyObject` using Swift's knuckle-whitening `unsafeBitCast()` function.
-
-#### Memory Management
-
-Since blocks can capture references to variables and `JSContext`s maintain strong references to all their variables, some care needs to be taken to avoid strong reference cycles. Avoid capturing your `JSContext` or any `JSValue`s inside a block. Instead, use `[JSContext currentContext]` to get the current context and pass any values you need as parameters.
-
-
-### `JSExport` Protocol
-
-Another way to use our custom objects from within JavaScript code is to add conformance to the `JSExport` protocol. Whatever properties, instance methods, and class methods we declare in our `JSExport`-inherited protocol will *automatically* be available to any JavaScript code. We'll see how in the following section.
-
-
-## JavaScriptCore in Practice
-
-Let's build out an example that will use all these different techniques—we'll define a `Person` model that conforms to the `JSExport` sub-protocol `PersonJSExports`, then use JavaScript to create and populate instances from a JSON file. Who needs `NSJSONSerialization` when there's an entire JavaScript VM lying around?
-
-### 1) `PersonJSExports` and `Person`
-
-Our `Person` class implements the `PersonJSExports` protocol, which specifies what properties should be available in JavaScript.
-
-> The `create...` class method is necessary because JavaScriptCore does *not* bridge initializers—we can't simply say `var person = new Person()` the way we would with a native JavaScript type.
-
-````swift
-// Custom protocol must be declared with `@objc`
-@objc protocol PersonJSExports : JSExport {
- var firstName: String { get set }
- var lastName: String { get set }
- var birthYear: NSNumber? { get set }
-
- func getFullName() -> String
-
- /// create and return a new Person instance with `firstName` and `lastName`
- static func createWithFirstName(firstName: String, lastName: String) -> Person
-}
-
-// Custom class must inherit from `NSObject`
-@objc class Person : NSObject, PersonJSExports {
- // properties must be declared as `dynamic`
- dynamic var firstName: String
- dynamic var lastName: String
- dynamic var birthYear: NSNumber?
-
- init(firstName: String, lastName: String) {
- self.firstName = firstName
- self.lastName = lastName
- }
-
- class func createWithFirstName(firstName: String, lastName: String) -> Person {
- return Person(firstName: firstName, lastName: lastName)
- }
-
- func getFullName() -> String {
- return "\(firstName) \(lastName)"
- }
-}
-````
-````objective-c
-// in Person.h -----------------
-@class Person;
-
-@protocol PersonJSExports
- @property (nonatomic, copy) NSString *firstName;
- @property (nonatomic, copy) NSString *lastName;
- @property NSInteger ageToday;
-
- - (NSString *)getFullName;
-
- // create and return a new Person instance with `firstName` and `lastName`
- + (instancetype)createWithFirstName:(NSString *)firstName lastName:(NSString *)lastName;
-@end
-
-@interface Person : NSObject
- @property (nonatomic, copy) NSString *firstName;
- @property (nonatomic, copy) NSString *lastName;
- @property NSInteger ageToday;
-@end
-
-// in Person.m -----------------
-@implementation Person
-- (NSString *)getFullName {
- return [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName];
-}
-
-+ (instancetype) createWithFirstName:(NSString *)firstName lastName:(NSString *)lastName {
- Person *person = [[Person alloc] init];
- person.firstName = firstName;
- person.lastName = lastName;
- return person;
-}
-@end
-````
-
-### 2) `JSContext` Configuration
-
-Before we can use the `Person` class we've created, we need to export it to the JavaScript environment. We'll also take this moment to import the [Mustache JS library](http://mustache.github.io/), which we'll use to apply templates to our `Person` objects later.
-
-````swift
-// export Person class
-context.setObject(Person.self, forKeyedSubscript: "Person")
-
-// load Mustache.js
-if let mustacheJSString = String(contentsOfFile:..., encoding:NSUTF8StringEncoding, error:nil) {
- context.evaluateScript(mustacheJSString)
-}
-````
-````objective-c
-// export Person class
-context[@"Person"] = [Person class];
-
-// load Mustache.js
-NSString *mustacheJSString = [NSString stringWithContentsOfFile:... encoding:NSUTF8StringEncoding error:nil];
-[context evaluateScript:mustacheJSString];
-````
-
-
-### 3) JavaScript Data & Processing
-
-Here's a look at our simple JSON example and the code that will process it to create new `Person` instances.
-
-> Note: JavaScriptCore translates Objective-C/Swift method names to be JavaScript-compatible. Since JavaScript doesn't have named parameters, any external parameter names are converted to camel-case and appended to the function name. In this example, the Objective-C method `createWithFirstName:lastName:` becomes `createWithFirstNameLastName()` in JavaScript.
-
-````JavaScript
-var loadPeopleFromJSON = function(jsonString) {
- var data = JSON.parse(jsonString);
- var people = [];
- for (i = 0; i < data.length; i++) {
- var person = Person.createWithFirstNameLastName(data[i].first, data[i].last);
- person.birthYear = data[i].year;
-
- people.push(person);
- }
- return people;
-}
-````
-````JSON
-[
- { "first": "Grace", "last": "Hopper", "year": 1906 },
- { "first": "Ada", "last": "Lovelace", "year": 1815 },
- { "first": "Margaret", "last": "Hamilton", "year": 1936 }
-]
-````
-
-
-### 4) Tying It All Together
-
-All that remains is to load the JSON data, call into the `JSContext` to parse the data into an array of `Person` objects, and render each `Person` using a Mustache template:
-
-````swift
-// get JSON string
-let peopleJSON = try! String(contentsOfFile: ..., encoding: NSUTF8StringEncoding)
-
-// get load function
-let load = context.objectForKeyedSubscript("loadPeopleFromJSON")
-// call with JSON and convert to an Array
-if let people = load.callWithArguments([peopleJSON]).toArray() as? [Person] {
-
- // get rendering function and create template
- let mustacheRender = context.objectForKeyedSubscript("Mustache").objectForKeyedSubscript("render")
- let template = "{% raw %}{{getFullName}}, born {{birthYear}}{% endraw %}"
-
- // loop through people and render Person object as string
- for person in people {
- print(mustacheRender.callWithArguments([template, person]))
- }
-}
-
-// Output:
-// Grace Hopper, born 1906
-// Ada Lovelace, born 1815
-// Margaret Hamilton, born 1936
-````
-````objective-c
-// get JSON string
-NSString *peopleJSON = [NSString stringWithContentsOfFile:... encoding:NSUTF8StringEncoding error:nil];
-
-// get load function
-JSValue *load = context[@"loadPeopleFromJSON"];
-// call with JSON and convert to an NSArray
-JSValue *loadResult = [load callWithArguments:@[peopleJSON]];
-NSArray *people = [loadResult toArray];
-
-// get rendering function and create template
-JSValue *mustacheRender = context[@"Mustache"][@"render"];
-NSString *template = @"{% raw %}{{getFullName}}, born {{birthYear}}{% endraw %}";
-
-// loop through people and render Person object as string
-for (Person *person in people) {
- NSLog(@"%@", [mustacheRender callWithArguments:@[template, person]]);
-}
-
-// Output:
-// Grace Hopper, born 1906
-// Ada Lovelace, born 1815
-// Margaret Hamilton, born 1936
-````
-
-
-* * *
-
-
-How can you use JavaScript in your apps? JavaScript snippets could be the basis for user-defined plugins that ship alongside yours. If your product started out on the web, you may have existing infrastructure that can be used with only minor changes. Or if *you* started out as a programmer on the web, you might relish the chance to get back to your scripty roots. Whatever the case, JavaScriptCore is too well-built and powerful to ignore.
-
-
diff --git a/2015-01-26-swift-objc-runtime.md b/2015-01-26-swift-objc-runtime.md
index a0763269..a2c9e420 100644
--- a/2015-01-26-swift-objc-runtime.md
+++ b/2015-01-26-swift-objc-runtime.md
@@ -80,7 +80,7 @@ extension UIViewController {
if didAddMethod {
class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
} else {
- method_exchangeImplementations(originalMethod, swizzledMethod)
+ method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
}
diff --git a/2015-02-02-ibinspectable-ibdesignable.md b/2015-02-02-ibinspectable-ibdesignable.md
index c9ef9ce9..af5dc621 100644
--- a/2015-02-02-ibinspectable-ibdesignable.md
+++ b/2015-02-02-ibinspectable-ibdesignable.md
@@ -1,11 +1,10 @@
---
-title: "IBInspectable / IBDesignable"
+title: "IBInspectable / IBDesignable"
category: Xcode
author: Nate Cook
excerpt: "Replacing an interface that requires us to memorize and type with one we can see and manipulate can be a enormous improvement. With `IBInspectable` and `IBDesignable`, Xcode 6 makes just such a substitution, building new interactions on top of old technologies."
status:
- swift: 2.0
- reviewed: December 15, 2015
+ swift: 1.0
---
Show, don't tell. Seeing is believing. A picture is worth a thousand emails words.
@@ -17,7 +16,7 @@ Whatever the cliché, replacing an interface that requires us to memorize and ty
`IBInspectable` properties provide new access to an old feature: user-defined runtime attributes. Currently accessible from the identity inspector, these attributes have been available since before Interface Builder was integrated into Xcode. They provide a powerful mechanism for configuring any key-value coded property of an instance in a NIB, XIB, or storyboard:
-
+
While powerful, runtime attributes can be cumbersome to work with. The key path, type, and value of an attribute need to be set on each instance, without any autocompletion or type hinting, which requires trips to the documentation or a custom subclass's source code to double-check the settings. `IBInspectable` properties solve this problem outright: in Xcode 6 you can now specify any property as inspectable and get a user interface built just for your custom class.
@@ -44,7 +43,7 @@ For example, these properties in a `UIView` subclass update the backing layer wi
Marked with `@IBInspectable` (or `IBInspectable` in Objective-C), they are easily editable in Interface Builder's inspector panel. Note that Xcode goes the extra mile here—property names are converted from camel- to title-case and related names are grouped together:
-
+
Since inspectable properties are simply an interface on top of user-defined runtime attributes, the same list of types is supported: booleans, strings, and numbers (i.e., `NSNumber` or any of the numeric value types), as well as `CGPoint`, `CGSize`, `CGRect`, `UIColor`, and `NSRange`, adding `UIImage` for good measure.
@@ -84,7 +83,7 @@ class MyCustomView: UIView {
}
````
-
+
The time-savings from this feature can't be overstated. Combined with `IBInspectable` properties, a designer or developer can easily tweak the rendering of a custom control to get the exact result she wants. Any changes, whether made in code or the attribute inspector, are immediately rendered on the canvas.
@@ -109,7 +108,7 @@ Since the custom view won't have the full context of your app when rendered in I
What can you create with a combination of `IBInspectable` attributes in your `IBDesignable` custom view? As an example, let's update an old classic from [Apple folklore](http://www.folklore.org/StoryView.py?story=Calculator_Construction_Set.txt): the "Steve Jobs Roll Your Own Calculator Construction Set," Xcode 6-style ([gist](https://gist.github.com/natecook1000/4269059121ec247fbb90)):
-
+
* * *
diff --git a/2015-02-09-swift-1.2.md b/2015-02-09-swift-1.2.md
index e9296206..7e885dfa 100644
--- a/2015-02-09-swift-1.2.md
+++ b/2015-02-09-swift-1.2.md
@@ -4,15 +4,14 @@ category: Swift
author: Nate Cook
tags: swift
excerpt: "Swift, true to its name, is moving fast. With the beta release of Swift 1.2, the Swift team has responded to so many of the community's requests in one fell swoop, we're overflowing with new and exciting features. This week, take a look at two major aspects of the release that will significantly improve the experience of working in Swift: first, big changes in `if let` optional binding, and second, new access to nullability annotations in Objective-C."
-hiddenlang: ""
+retired: true
status:
- swift: 1.2
+ swift: 1.2
---
-Swift, true to its name, is moving fast. This week marks the beta release of Swift 1.2, a *major* update to the language. The Swift team has responded to so many of the community's requests in one fell swoop, we're overflowing with new and exciting features. Every line-item in this announcement is a big advancement: incremental builds, improved error messages and stability in Xcode, static class properties, support for C unions and bitfields, bridging of Swift `enum`s to Objective-C, safer type casting, improvements to single-line closures, and more.
-
-In what follows, let's look at two major aspects of the release that will significantly improve the experience of working in Swift: first, big changes in `if let` optional binding (*"finally"*), and second, new access to nullability annotations in Objective-C.
+Swift, true to its name, is moving fast. This week marks the beta release of Swift 1.2, a _major_ update to the language. The Swift team has responded to so many of the community's requests in one fell swoop, we're overflowing with new and exciting features. Every line-item in this announcement is a big advancement: incremental builds, improved error messages and stability in Xcode, static class properties, support for C unions and bitfields, bridging of Swift `enum`s to Objective-C, safer type casting, improvements to single-line closures, and more.
+In what follows, let's look at two major aspects of the release that will significantly improve the experience of working in Swift: first, big changes in `if let` optional binding (_"finally"_), and second, new access to nullability annotations in Objective-C.
## Improved Optional Binding
@@ -45,12 +44,12 @@ if let a = a, b = b, c = c where c != 0 {
```
> The order of execution in these two examples is identical. Using the new syntax, each binding is evaluated in turn, stopping if any of the attempted bindings is `nil`. Only after all the optional bindings are successful is the `where` clause checked.
->
-> An `if` statement can actually have more than one `let` binding separated by commas. Since each `let` binding can bind multiple optionals *and* include a `where` clause, some truly sophisticated logic is possible with this construct. (Thanks to [Stephen Celis](https://twitter.com/stephencelis) for helping clarify this point.)
+>
+> An `if` statement can actually have more than one `let` binding separated by commas. Since each `let` binding can bind multiple optionals _and_ include a `where` clause, some truly sophisticated logic is possible with this construct. (Thanks to [Stephen Celis](https://twitter.com/stephencelis) for helping clarify this point.)
What's more, later binding expressions can reference earlier bindings. This means you can delve into `Dictionary` instances or cast an `AnyObject?` value to a specific type, then use it in another expression, all in a single `if let` statement.
-To revisit the canonical example, this is what parsing a [big block of JSON](http://jsonplaceholder.typicode.com/users) can look like in Swift 1.2. The example uses one `if let` block to handle the optionals that come with using `NSBundle`, `NSURL` and `NSData`, then another to map several `AnyObject?` instances from the interpreted JSON to specific types:
+To revisit the canonical example, this is what parsing a [big block of JSON](http://jsonplaceholder.typicode.com/users) can look like in Swift 1.2. The example uses one `if let` block to handle the optionals that come with using `NSBundle`, `NSURL` and `NSData`, then another to map several `AnyObject?` instances from the interpreted JSON to specific types:
```swift
var users: [User] = []
@@ -60,7 +59,7 @@ if let
path = NSBundle.mainBundle().pathForResource("users", ofType: "json"),
url = NSURL(fileURLWithPath: path),
data = NSData(contentsOfURL: url),
- userList = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: nil) as? [[String: AnyObject]]
+ userList = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: nil) as? [[String: AnyObject]]
{
// extract individual users
for userDict in userList {
@@ -75,6 +74,7 @@ if let
}
}
```
+
```JSON
[
{
@@ -129,8 +129,6 @@ if let
I see many commas in our future.
-
-
## Nullability Annotations
When Swift was first released, every call to a Cocoa API method took and returned implicitly unwrapped optionals (i.e., `AnyObject!`). Because they crash a program on access, implicitly unwrapped return values are inherently unsafe if there isn't clear documentation of whether or not a method will return a null value. All those exclamation marks were a sign of bad form. Sure, Swift bridged to the Cocoa APIs, but it always looked grumpy about it.
@@ -150,7 +148,7 @@ The first three annotations can also be used with C pointers and block pointers,
As an example to show the benefit of these annotations, let's take a look at a data controller used to handle a list of locations, each with a possible attached photo:
-```objective-c
+```objc
@interface LocationDataController : NSObject
@property (nonatomic, readonly) NSArray *locations;
@@ -167,7 +165,7 @@ Without any nullability annotations, each pointer in my `LocationDataController`
class LocationDataController : NSObject {
var locations: [AnyObject]! { get }
var latestLocation: Location! { get }
-
+
func addPhoto(photo: Photo!, forLocation location: Location!)
func photoForLocation(location: Location!) -> Photo!
}
@@ -175,7 +173,7 @@ class LocationDataController : NSObject {
Enough! With! The shouting! Here's how I can now annotate the Objective-C interface:
-```objective-c
+```objc
@interface LocationDataController : NSObject
@property (nonnull, nonatomic, readonly) NSArray *locations;
@@ -186,13 +184,13 @@ Enough! With! The shouting! Here's how I can now annotate the Objective-C interf
@end
```
-First, the properties—my `locations` list is `nonnull`, since at worst it will be an empty array, but `latestLocation` *can* be `nil` if there are no locations in the list yet. Likewise, the parameters to my two methods should always have a value, yet because not all locations have a photo, that second method returns a `nullable` photo. Back in Swift, the results are much better—that is, clearer about how to safely use the data controller and no more grumpy `!`s:
+First, the properties—my `locations` list is `nonnull`, since at worst it will be an empty array, but `latestLocation` _can_ be `nil` if there are no locations in the list yet. Likewise, the parameters to my two methods should always have a value, yet because not all locations have a photo, that second method returns a `nullable` photo. Back in Swift, the results are much better—that is, clearer about how to safely use the data controller and no more grumpy `!`s:
```swift
class LocationDataController : NSObject {
var locations: [AnyObject] { get }
var latestLocation: Location? { get }
-
+
func addPhoto(photo: Photo, forLocation location: Location)
func photoForLocation(location: Location) -> Photo?
}
@@ -200,11 +198,11 @@ class LocationDataController : NSObject {
### NS_ASSUME_NONNULL_BEGIN/END
-Annotating *any* pointer in an Objective-C header file causes the compiler to expect annotations for the entire file, bringing on a cascade of warnings. Given that most annotations will be `nonnull`, a new macro can help streamline the process of annotating existing classes. Simply mark the beginning and end of a section of your header with `NS_ASSUME_NONNULL_BEGIN` and `..._END`, then mark the exceptions.
+Annotating _any_ pointer in an Objective-C header file causes the compiler to expect annotations for the entire file, bringing on a cascade of warnings. Given that most annotations will be `nonnull`, a new macro can help streamline the process of annotating existing classes. Simply mark the beginning and end of a section of your header with `NS_ASSUME_NONNULL_BEGIN` and `..._END`, then mark the exceptions.
Another revision of our data controller interface from above results in a more readable version with the exact same Swift profile:
-```objective-c
+```objc
@interface LocationDataController : NSObject
NS_ASSUME_NONNULL_BEGIN
@@ -222,18 +220,15 @@ NS_ASSUME_NONNULL_END
The new Objective-C nullability annotations have huge benefits for code on the Swift side of the fence, but there's a substantial gain here even without writing a line of Swift. Pointers marked as `nonnull` will now give a hint during autocomplete and yield a warning if sent `nil` instead of a proper pointer:
-```objective-c
+```objc
// Can I remove a photo by sending nil?
[dataController addPhoto:nil forLocation:currentLocation];
// Nope -- Warning: Null passed to a callee that requires a non-null argument
```
-* * *
-
-Excitingly, all that is just half the story. In addition to the changes in Swift syntax and compiler savoir faire, the standard library has also seen a [major revision](http://swiftdoc.org/news/2015/02/swift1.2/), including a proper `Set` class (so long, [dear friend](https://github.com/natecook1000/SwiftSets/blob/master/Set.swift)). *Okaaay,* so none of our code works anymore, and Stack Overflow has 21,000 out-of-date Swift questions? It's still fun to be along for the ride.
-
-
+---
+Excitingly, all that is just half the story. In addition to the changes in Swift syntax and compiler savoir faire, the standard library has also seen a [major revision](http://swiftdoc.org/news/2015/02/swift1.2/), including a proper `Set` class (so long, [dear friend](https://github.com/natecook1000/SwiftSets/blob/master/Set.swift)). _Okaaay,_ so none of our code works anymore, and Stack Overflow has 21,000 out-of-date Swift questions? It's still fun to be along for the ride.
+{% asset 'articles/mapkit-js.js' async="async" %}
+```
+
+With only a few lines of JavaScript and HTML,
+we can embed a beautiful little map into our webpages
+using MapKit JS.
+
+
+
+{% asset 'articles/mapkit-js.js' async="async" %}
+
+{% warning %}
+Embedding a MapKit JS map on a website with a
+[Content Security Policy (CSP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)?
+Be sure to add `https://*.apple-mapkit.com` to your
+`script-src`, `img-src`, and `connect-src` directives.
+{% endwarning %}
+
+---
+
+MapKit JS joins a robust ecosystem of mapping providers.
+Currently in beta,
+it offers
+250,000 map initializations and
+25,000 service requests
+for free _per day_ (!),
+which is quite generous ---
+especially when compared to similar offerings
+from [Google](https://cloud.google.com/maps-platform/pricing/)
+and [MapBox](https://www.mapbox.com/pricing/).
+
+So if you've been kicking around an idea for
+a map widget to add to your site
+or a directions feature for your web app,
+you might want to give MapKit JS a look!
diff --git a/2019-03-18-swift-foundation-error-protocols.md b/2019-03-18-swift-foundation-error-protocols.md
new file mode 100644
index 00000000..e7091e42
--- /dev/null
+++ b/2019-03-18-swift-foundation-error-protocols.md
@@ -0,0 +1,681 @@
+---
+title: LocalizedError, RecoverableError, CustomNSError
+author: Mattt
+category: Cocoa
+excerpt: >-
+ We're all familiar with the `Error` type,
+ but have you met these related Swift Foundation error protocols?
+status:
+ swift: 5.0
+---
+
+Swift 2 introduced error handling by way of the
+`throws`, `do`, `try` and `catch` keywords.
+It was designed to work hand-in-hand with
+Cocoa [error handling conventions](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ErrorHandlingCocoa/ErrorHandling/ErrorHandling.html#//apple_ref/doc/uid/TP40001806-CH201-SW1),
+such that any type conforming to the `ErrorProtocol` protocol
+(since renamed to `Error`)
+was implicitly bridged to `NSError` and
+Objective-C methods with an `NSError**` parameter,
+were imported by Swift as throwing methods.
+
+```objc
+- (NSURL *)replaceItemAtURL:(NSURL *)url
+ options:(NSFileVersionReplacingOptions)options
+ error:(NSError * _Nullable *)error;
+```
+
+```swift
+func replaceItem(at url: URL,
+ options: NSFileVersion.ReplacingOptions = []) throws -> URL
+```
+
+For the most part,
+these changes offered a dramatic improvement over the status quo
+(namely, _no error handling conventions in Swift at all_).
+However, there were still a few gaps to fill
+to make Swift errors fully interoperable with Objective-C types.
+as described by Swift Evolution proposal
+[SE-0112: "Improved NSError Bridging"](https://github.com/apple/swift-evolution/blob/master/proposals/0112-nserror-bridging.md).
+
+Not long after these refinements landed in Swift 3,
+the practice of declaring errors in enumerations
+had become idiomatic.
+
+Yet for how familiar we've all become with
+`Error` (née `ErrorProtocol`),
+surprisingly few of us are on a first-name basis with
+the other error protocols to come out of SE-0112.
+Like, when was the last time you came across `LocalizedError` in the wild?
+How about `RecoverableError`?
+`CustomNSError` _qu'est-ce que c'est_?
+
+At the risk of sounding cliché,
+you might say that these protocols are indeed pretty obscure,
+and there's a good chance you haven't heard of them:
+
+
+
`LocalizedError`
+
+A specialized error that provides
+localized messages describing the error and why it occurred.
+
+
+
`RecoverableError`
+
+A specialized error that may be recoverable
+by presenting several potential recovery options to the user.
+
+
+
`CustomNSError`
+
+A specialized error that provides a
+domain, error code, and user-info dictionary.
+
+
+
+If you haven't heard of any of these until now,
+you may be wondering when you'd ever use them.
+Well, as the adage goes,
+_"There's no time like the present"_.
+
+This week on NSHipster,
+we'll take a quick look at each of these Swift Foundation error protocols
+and demonstrate how they can make your code ---
+if not _less_ error-prone ---
+then more enjoyable in its folly.
+
+---
+
+## Communicating Errors to the User
+
+> Too many cooks spoil the broth.
+
+Consider the following `Broth` type
+with a nested `Error` enumeration
+and an initializer that takes a number of cooks
+and throws an error if that number is inadvisably large:
+
+```swift
+struct Broth {
+ enum Error {
+ case tooManyCooks(Int)
+ }
+
+ init(numberOfCooks: Int) throws {
+ precondition(numberOfCooks > 0)
+ guard numberOfCooks < <#redacted#> else {
+ throw Error.tooManyCooks(numberOfCooks)
+ }
+
+ // ... proceed to make broth
+ }
+}
+```
+
+If an iOS app were to communicate an error
+resulting from broth spoiled by multitudinous cooks,
+it might do so
+with by presenting a `UIAlertController`
+in a `catch` statement like this:
+
+```swift
+import UIKit
+
+class ViewController: UIViewController {
+ override func viewDidAppear(_ animated: Bool) {
+ super.viewDidAppear(animated)
+
+ do {
+ self.broth = try Broth(numberOfCooks: 100)
+ } catch let error as Broth.Error {
+ let title: String
+ let message: String
+
+ switch error {
+ case .tooManyCooks(let numberOfCooks):
+ title = "Too Many Cooks (\(numberOfCooks))"
+ message = """
+ It's difficult to reconcile many opinions.
+
+ Reduce the number of decision makers.
+ """
+ }
+
+ let alertController =
+ UIAlertController(title: title,
+ message: message,
+ preferredStyle: .alert)
+ alertController.addAction(
+ UIAlertAction(title: "OK",
+ style: .default)
+ )
+
+ self.present(alertController, animated: true, completion: nil)
+ } catch {
+ // handle other errors...
+ }
+ }
+}
+```
+
+Such an implementation, however,
+is at odds with well-understood boundaries between models and controllers.
+Not only does it create bloat in the controller,
+it also doesn't scale to handling multiple errors
+or handling errors in multiple contexts.
+
+To reconcile these anti-patterns,
+let's turn to our first Swift Foundation error protocol.
+
+### Adopting the LocalizedError Protocol
+
+The `LocalizedError` protocol inherits the base `Error` protocol
+and adds four instance property requirements.
+
+```swift
+protocol LocalizedError : Error {
+ var errorDescription: String? { get }
+ var failureReason: String? { get }
+ var recoverySuggestion: String? { get }
+ var helpAnchor: String? { get }
+}
+```
+
+These properties map 1:1 with familiar `NSError` `userInfo` keys.
+
+| Requirement | User Info Key |
+| -------------------- | --------------------------------------- |
+| `errorDescription` | `NSLocalizedDescriptionKey` |
+| `failureReason` | `NSLocalizedFailureReasonErrorKey` |
+| `recoverySuggestion` | `NSLocalizedRecoverySuggestionErrorKey` |
+| `helpAnchor` | `NSHelpAnchorErrorKey` |
+
+Let's take another pass at our nested `Broth.Error` type
+and see how we might refactor error communication from the controller
+to instead be concerns of `LocalizedError` conformance.
+
+```swift
+import Foundation
+
+extension Broth.Error: LocalizedError {
+ var errorDescription: String? {
+ switch self {
+ case .tooManyCooks(let numberOfCooks):
+ return "Too Many Cooks (\(numberOfCooks))"
+ }
+ }
+
+ var failureReason: String? {
+ switch self {
+ case .tooManyCooks:
+ return "It's difficult to reconcile many opinions."
+ }
+ }
+
+ var recoverySuggestion: String? {
+ switch self {
+ case .tooManyCooks:
+ return "Reduce the number of decision makers."
+ }
+ }
+}
+```
+
+{% info %}
+Thanks to default protocol implementations,
+you don't have to satisfy every requirement to adopt `LocalizedError`.
+In this example, we omit the `helpAnchor` property
+(which is pretty much irrelevant on iOS anyway).
+{% endinfo %}
+
+Using `switch` statements may be overkill
+for a single-case enumeration such as this,
+but it demonstrates a pattern that can be extended
+for more complex error types.
+Note also how pattern matching is used
+to bind the `numberOfCooks` constant to the associated value
+only when it's necessary.
+
+Now we can
+
+```swift
+import UIKit
+
+class ViewController: UIViewController {
+ override func viewDidAppear(_ animated: Bool) {
+ super.viewDidAppear(animated)
+
+ do {
+ try makeBroth(numberOfCooks: 100)
+ } catch let error as LocalizedError {
+ let title = error.errorDescription
+ let message = [
+ error.failureReason,
+ error.recoverySuggestion
+ ].compactMap { $0 }
+ .joined(separator: "\n\n")
+
+ let alertController =
+ UIAlertController(title: title,
+ message: message,
+ preferredStyle: .alert)
+ alertController.addAction(
+ UIAlertAction(title: "OK",
+ style: .default)
+ )
+
+ self.present(alertController, animated: true, completion: nil)
+ } catch {
+ // handle other errors...
+ }
+ }
+}
+```
+
+{% asset swift-foundation-error-uikit-alert-modal.png alt="iOS alert modal" %}
+
+{% info %}
+You could further DRY up your code
+by creating a convenience initializer for `UIAlertController`.
+
+```swift
+extension UIAlertController {
+ convenience init(_ error: Error,
+ preferredStyle: UIAlertController.Style)
+ where Error: LocalizedError
+ {
+ let title = error.errorDescription
+ let message = [
+ error.failureReason,
+ error.recoverySuggestion
+ ].compactMap { $0 }
+ .joined(separator: "\n\n")
+
+ self.init(title: title,
+ message: message,
+ preferredStyle: preferredStyle)
+ }
+}
+```
+
+{% endinfo %}
+
+---
+
+If that seems like a lot of work just to communicate an error to the user...
+you might be onto something.
+
+Although UIKit borrowed many great conventions and idioms from AppKit,
+error handling wasn't one of them.
+By taking a closer look at what was lost in translation,
+we'll finally have the necessary context to understand
+the two remaining error protocols to be discussed.
+
+---
+
+## Communicating Errors on macOS
+
+> If at first you don't succeed, try, try again.
+
+Communicating errors to users is significantly easier on macOS than on iOS.
+For example,
+you might construct and pass an `NSError` object
+to the `presentError(_:)` method,
+called on an `NSWindow`.
+
+```swift
+import AppKit
+
+@NSApplicationMain
+class AppDelegate: NSObject, NSApplicationDelegate {
+ @IBOutlet weak var window: NSWindow!
+
+ func applicationDidFinishLaunching(_ aNotification: Notification) {
+ do {
+ _ = try something()
+ } catch {
+ window.presentError(error)
+ }
+ }
+
+ func something() throws -> Never {
+ let userInfo: [String: Any] = [
+ NSLocalizedDescriptionKey:
+ NSLocalizedString("The operation couldn’t be completed.",
+ comment: "localizedErrorDescription"),
+ NSLocalizedRecoverySuggestionErrorKey:
+ NSLocalizedString("If at first you don't succeed...",
+ comment: "localizedErrorRecoverSuggestion")
+ ]
+
+ throw NSError(domain: "com.nshipster.error", code: 1, userInfo: userInfo)
+ }
+}
+```
+
+Doing so presents a modal alert dialog
+that fits right in with the rest of the system.
+
+{% asset swift-foundation-error-appkit-default-modal.png alt="Default macOS error modal" %}
+
+But macOS error handling isn't merely a matter of convenient APIs;
+it also has built-in mechanisms for allowing
+users to select one of several options
+to attempt to resolve the reported issue.
+
+### Recovering from Errors
+
+To turn a conventional `NSError` into
+one that supports recovery,
+you specify values for the `userInfo` keys
+`NSLocalizedRecoveryOptionsErrorKey` and `NSRecoveryAttempterErrorKey`.
+A great way to do that
+is to override the `application(_:willPresentError:)` delegate method
+and intercept and modify an error before it's presented to the user.
+
+```swift
+extension AppDelegate {
+ func application(_ application: NSApplication,
+ willPresentError error: Error) -> Error
+ {
+ var userInfo: [String: Any] = (error as NSError).userInfo
+ userInfo[NSLocalizedRecoveryOptionsErrorKey] = [
+ NSLocalizedString("Try, try again",
+ comment: "tryAgain")
+ NSLocalizedString("Give up too easily",
+ comment: "giveUp")
+ ]
+ userInfo[NSRecoveryAttempterErrorKey] = self
+
+ return NSError(domain: (error as NSError).domain,
+ code: (error as NSError).code,
+ userInfo: userInfo)
+ }
+}
+```
+
+For `NSLocalizedRecoveryOptionsErrorKey`,
+specify an array of one or more localized strings
+for each recovery option available the user.
+
+For `NSRecoveryAttempterErrorKey`,
+set an object that implements the
+`attemptRecovery(fromError:optionIndex:)` method.
+
+```swift
+extension AppDelegate {
+ // MARK: NSErrorRecoveryAttempting
+ override func attemptRecovery(fromError error: Error,
+ optionIndex recoveryOptionIndex: Int) -> Bool
+ {
+ do {
+ switch recoveryOptionIndex {
+ case 0: // Try, try again
+ try something()
+ case 1:
+ fallthrough
+ default:
+ break
+ }
+ } catch {
+ window.presentError(error)
+ }
+
+ return true
+ }
+}
+```
+
+With just a few lines of code,
+you're able to facilitate a remarkably complex interaction,
+whereby a user is alerted to an error and prompted to resolve it
+according to a set of available options.
+
+{% asset swift-foundation-error-appkit-fixed-modal.png alt="Recoverable macOS error modal" %}
+
+Cool as that is,
+it carries some pretty gross baggage.
+First,
+the `attemptRecovery` requirement is part of an
+informal protocol,
+which is effectively a handshake agreement
+that things will work as advertised.
+Second,
+the use of option indexes instead of actual objects
+makes for code that's as fragile as it is cumbersome to write.
+
+Fortunately,
+we can significantly improve on this
+by taking advantage of Swift's superior type system
+and (at long last) the second subject of this article.
+
+### Modernizing Error Recovery with RecoverableError
+
+The `RecoverableError` protocol,
+like `LocalizedError` is a refinement on the base `Error` protocol
+with the following requirements:
+
+```swift
+protocol RecoverableError : Error {
+ var recoveryOptions: [String] { get }
+
+ func attemptRecovery(optionIndex recoveryOptionIndex: Int, resultHandler handler: @escaping (Bool) -> Void)
+ func attemptRecovery(optionIndex recoveryOptionIndex: Int) -> Bool
+}
+```
+
+Also like `LocalizedError`,
+these requirements map onto error `userInfo` keys
+(albeit not as directly).
+
+| Requirement | User Info Key |
+| ----------------------------------------------------------------------- | ------------------------------------------- |
+| `recoveryOptions` | `NSLocalizedRecoveryOptionsErrorKey` |
+| `attemptRecovery(optionIndex:_:)` `attemptRecovery(optionIndex:)` | `NSRecoveryAttempterErrorKey` \* |
+
+The `recoveryOptions` property requirement
+is equivalent to the `NSLocalizedRecoveryOptionsErrorKey`:
+an array of strings that describe the available options.
+
+The `attemptRecovery` functions
+formalize the previously informal delegate protocol;
+`func attemptRecovery(optionIndex:)`
+is for "application" granularity,
+whereas
+`attemptRecovery(optionIndex:resultHandler:)`
+is for "document" granularity.
+
+### Supplementing RecoverableError with Additional Types
+
+On its own,
+the `RecoverableError` protocol improves only slightly on
+the traditional, `NSError`-based methodology
+by formalizing the requirements for recovery.
+
+Rather than implementing conforming types individually,
+we can generalize the functionality
+with some clever use of generics.
+
+First,
+define an `ErrorRecoveryDelegate` protocol
+that re-casts the `attemptRecovery` methods from before
+to use an associated, `RecoveryOption` type.
+
+```swift
+protocol ErrorRecoveryDelegate: class {
+ associatedtype RecoveryOption: CustomStringConvertible,
+ CaseIterable
+
+ func attemptRecovery(from error: Error,
+ with option: RecoveryOption) -> Bool
+}
+```
+
+Requiring that `RecoveryOption` conforms to `CaseIterable`,
+allows us to vend options directly to API consumers
+independently of their presentation to the user.
+
+From here,
+we can define a generic `DelegatingRecoverableError` type
+that wraps an `Error` type
+and associates it with the aforementioned `Delegate`,
+which is responsible for providing recovery options
+and attempting recovery with the one selected.
+
+```swift
+struct DelegatingRecoverableError: RecoverableError
+ where Delegate: ErrorRecoveryDelegate,
+ Error: Swift.Error
+{
+ let error: Error
+ weak var delegate: Delegate? = nil
+
+ init(recoveringFrom error: Error, with delegate: Delegate?) {
+ self.error = error
+ self.delegate = delegate
+ }
+
+ var recoveryOptions: [String] {
+ return Delegate.RecoveryOption.allCases.map { "\($0)" }
+ }
+
+ func attemptRecovery(optionIndex recoveryOptionIndex: Int) -> Bool {
+ let recoveryOptions = Delegate.RecoveryOption.allCases
+ let index = recoveryOptions.index(recoveryOptions.startIndex,
+ offsetBy: recoveryOptionIndex)
+ let option = Delegate.RecoveryOption.allCases[index]
+
+ return self.delegate?.attemptRecovery(from: self.error,
+ with: option) ?? false
+ }
+}
+```
+
+Now we can refactor the previous example of our macOS app
+to have `AppDelegate` conform to `ErrorRecoveryDelegate`
+and define a nested `RecoveryOption` enumeration
+with all of the options we wish to support.
+
+```swift
+extension AppDelegate: ErrorRecoveryDelegate {
+ enum RecoveryOption: String, CaseIterable, CustomStringConvertible {
+ case tryAgain
+ case giveUp
+
+ var description: String {
+ switch self {
+ case .tryAgain:
+ return NSLocalizedString("Try, try again",
+ comment: self.rawValue)
+ case .giveUp:
+ return NSLocalizedString("Give up too easily",
+ comment: self.rawValue)
+ }
+ }
+ }
+
+ func attemptRecovery(from error: Error,
+ with option: RecoveryOption) -> Bool
+ {
+ do {
+ if option == .tryAgain {
+ try something()
+ }
+ } catch {
+ window.presentError(error)
+ }
+
+ return true
+ }
+
+ func application(_ application: NSApplication, willPresentError error: Error) -> Error {
+ return DelegatingRecoverableError(recoveringFrom: error, with: self)
+ }
+}
+```
+
+The result?
+
+{% asset swift-foundation-error-appkit-unrefined-modal.png alt="Recoverable macOS error modal with Unintelligible title" %}
+
+_...wait, that's not right._
+
+What's missing?
+To find out,
+let's look at our third and final protocol in our discussion.
+
+## Improving Interoperability with Cocoa Error Handling System
+
+The `CustomNSError` protocol
+is like an inverted `NSError`:
+it allows a type conforming to `Error`
+to act like it was instead an `NSError` subclass.
+
+```swift
+protocol CustomNSError: Error {
+ static var errorDomain: String { get }
+ var errorCode: Int { get }
+ var errorUserInfo: [String : Any] { get }
+}
+```
+
+The protocol requirements correspond to the
+`domain`, `code`, and `userInfo` properties of an `NSError`, respectively.
+
+Now, back to our modal from before:
+normally, the title is taken from `userInfo` via `NSLocalizedDescriptionKey`.
+Types conforming to `LocalizedError` can provide this too
+through their equivalent `errorDescription` property.
+And while we _could_ extend `DelegatingRecoverableError`
+to adopt `LocalizedError`,
+it's actually much less work to add conformance for `CustomNSError`:
+
+```swift
+extension DelegatingRecoverableError: CustomNSError {
+ var errorUserInfo: [String: Any] {
+ return (self.error as NSError).userInfo
+ }
+}
+```
+
+With this one additional step,
+we can now enjoy the fruits of our burden.
+
+{% asset swift-foundation-error-appkit-fixed-modal.png alt="Recoverable macOS error modal" %}
+
+{% info %}
+
+For extra credit,
+you can provide an implementation for the static `errorDomain` requirement
+through a conditional extension on `DelegatingRecoverableError`
+whose constrained `Error` type is itself `CustomNSError` (or an `NSError`).
+
+```swift
+extension DelegatingRecoverableError where Error: CustomNSError {
+ static var errorDomain: String {
+ return Error.errorDomain
+ }
+}
+```
+
+{% endinfo %}
+
+---
+
+In programming,
+it's often not what you know,
+but what you know _about_.
+Now that you're aware of the existence of
+`LocalizedError`, `RecoverableError`, `CustomNSError`,
+you'll be sure to identify situations in which they might
+improve error handling in your app.
+
+Useful AF, amiright?
+Then again,
+_"Familiarity breeds contempt"_;
+so often,
+what initially endears one to ourselves
+is what ultimately causes us to revile it.
+
+Such is the error of our ways.
diff --git a/2019-03-25-xcode-source-extensions.md b/2019-03-25-xcode-source-extensions.md
new file mode 100644
index 00000000..ef7f36e7
--- /dev/null
+++ b/2019-03-25-xcode-source-extensions.md
@@ -0,0 +1,489 @@
+---
+title: XcodeKit and Xcode Source Editor Extensions
+author: Zoë Smith
+category: Xcode
+excerpt: >-
+ When we last wrote about extending Xcode,
+ we were living in a golden age, and didn't even know it.
+ Plugins allowed us to tweak pretty much everything about Xcode;
+ Source Editor Extensions? Not so much.
+status:
+ swift: 5.0
+---
+
+When we last [wrote about extending Xcode](/xcode-plugins/) in 2014,
+we were living in a golden age, and didn't even know it.
+
+Back then,
+Xcode was a supposedly impenetrable castle that we'd leaned a couple of ladders against.
+Like a surprisingly considerate horde,
+we scaled the walls and got to work on some much-needed upkeep.
+Those were heady days of
+in-process code injection, an informally sanctioned and thriving ecosystem of third-party plugins ---
+all backed up by an in-app package manager.
+For a while, Apple tolerated it all.
+But with the introduction of [System Integrity Protection](https://support.apple.com/en-us/HT204899) in 2016,
+the ladders were abruptly kicked away.
+(Pour one out for [Alcatraz](https://github.com/alcatraz/Alcatraz) why don't we,
+with a chaser for [XcodeColors](https://github.com/robbiehanson/XcodeColors).
+Miss you buddy.)
+
+Plugins allowed us to tweak pretty much everything about Xcode:
+window layout, syntactic and semantic highlighting, changing UI elements,
+boilerplate generation, project analysis, bindings for something called Vim (?).
+Looking back at NSHipster's favorites, some are now thankfully included as a standard feature:
+inserting documentation comments,
+`switch` statement autocompletion
+or --- astonishingly --- line breaks in the issue navigator.
+Most of the inventive functionality that plugins added, though, has just plain gone.
+
+{% info %}
+While you _can_ re-sign Xcode to load compatible plugins today,
+you can't use this copy to distribute on the App Store.
+And besides, the plugin party is now well over.
+{% endinfo %}
+
+Xcode 8 proposed a solution for the missing plugins in the form of [Source Editor Extensions](https://developer.apple.com/videos/play/wwdc2016/414/).
+Like other macOS extensions, they can be sold via the App Store or distributed independently.
+But some bad, if old, news:
+unlike plugins, these new extensions are seriously limited in scope.
+They allow
+**pure text manipulation, instigated by the user from a menu command, on one source file at a time** —
+none of the fun stuff, in other words.
+
+Source Editor Extensions have remained unchanged since introduction.
+We'll discuss signs that _might_ point to interesting future developments.
+But if IDEs with an open attitude are more your thing,
+there's not much to see here yet.
+
+Let's start, though, by looking at the official situation today:
+
+## Source Editor Extensions
+
+By now, Apple platform developers will be familiar with extension architecture:
+separate binaries, sandboxed and running in their own process,
+but not distributable without a containing app.
+
+Compared to using a tool like Homebrew, installation is undoubtedly a pain:
+
+{% asset "xcode-source-editor-extension-installation-flow.png" alt="Flow diagram for extension installation process" %}
+
+After finding, downloading and launching the containing app,
+the extension shows up in the Extensions pane of System Preferences.
+You can then activate it,
+restart Xcode
+and it should manifest itself as a menu item.
+(App Store reviewers _love_ this process.)
+
+That's the finished result.
+To understand how you get to that point,
+let's create a simple extension of our own.
+This [sample project](https://github.com/zoejessica/marked)
+transforms `TODO`, `FIXME` and `MARK` code marks to be uppercased with a trailing colon,
+so Xcode can recognize them and add them to the quick navigation bar.
+(It's just one of the rules more fully implemented by the [SwiftFormat](https://github.com/nicklockwood/SwiftFormat) extension.)
+
+## Creating a Source Editor Extension
+
+Create a new Cocoa app as the containing app,
+and add a new target using the Xcode Source Editor Extension template.
+
+{% asset "xcode-source-editor-add-extension-target" alt="Screenshot of adding Source Editor Extension target to Xcode project" %}
+
+{% info %}
+In [Apple's terminology](https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG/ExtensionOverview.html#//apple_ref/doc/uid/TP40014214-CH2-SW2),
+the host app is
+the application that _calls_ your extension to do some useful work:
+in this case,
+the host is Xcode.
+The containing app is
+the new application that _you_ create to wrap extensions
+which can't stand by themselves.
+{% endinfo %}
+
+The target contains ready-made `XCSourceEditorExtension` and `XCSourceEditorCommand` subclasses,
+with a configured property list.
+
+Both of those superclasses are part of the [XcodeKit framework](https://developer.apple.com/documentation/xcodekit)
+(hence the `XC` prefix),
+which provides extensions the ability to modify the text and selections of a source file.
+
+### Display Names
+
+User-facing strings for an extension are sprinkled around the extension's `Info.plist` or defined at runtime:
+
+| Display text | Property | Definition |
+| ---------------------------------------------- | -------------------------------------------- | ---------- |
+| Extension name, as shown in System Preferences | `Bundle Display Name` | Info.plist |
+| Top level menu item for extension | `Bundle Name` | Info.plist |
+| Individual menu command | `XCSourceEditorCommandName` | Info.plist |
+| | `XCSourceEditorCommandDefinitionKey.nameKey` | Runtime |
+
+### Menu Items
+
+The only way a user can interact with an extension is by selecting one of its menu items.
+These show up at the bottom of the Editor menu when viewing a source code file.
+Xcode's one affordance to users is that keybindings can be assigned to extension commands,
+just as for other menu items.
+
+Each command gets a stringly-typed identifier, display text, and a class to handle it,
+which are each defined in the extension target's `Info.plist`.
+Alternatively,
+we can override these at runtime
+by providing a `commandDefinitions` property on the `XCSourceEditorExtension` subclass.
+The commands can all be funneled to a single `XCSourceEditorCommand` subclass
+or split up to be handled by multiple classes --- whichever you prefer.
+
+In our extension, we just define a single "Format Marks" menu item:
+
+```swift
+ var commandDefinitions: [[XCSourceEditorCommandDefinitionKey: Any]] {
+ let namespace = Bundle(for: type(of: self)).bundleIdentifier!
+ let marker = MarkerCommand.className()
+ return [[.identifierKey: namespace + marker,
+ .classNameKey: marker,
+ .nameKey: NSLocalizedString("Format Marks",
+ comment: "format marks menu item")]]
+ }
+```
+
+{% warning %}
+There is no way to dynamically _disable_ individual menu commands,
+for example, if an extension wanted to offer different functionality according to the type of source file.
+The extension is kept running for Xcode's lifetime,
+and menu items are determined at launch.
+Nor is there a way to suggest default key bindings.
+{% endwarning %}
+
+When the user chooses one of the menu commands defined by the extension,
+the handling class is called with
+[`perform(with:completionHandler:)`](https://developer.apple.com/documentation/xcodekit/xcsourceeditorcommand/2097278-perform).
+The extension finally gets access to something useful,
+namely the contents of the current source code file.
+
+### Inputs and Outputs
+
+The passed `XCSourceEditorCommandInvocation` argument holds a reference to the `XCSourceTextBuffer`,
+which gives us access to:
+
+- `completeBuffer`, containing the entire text of the file as a single `String`
+- another view on the same text, separated into `lines` of code
+- an array of current `selections` in terms of lines and columns, supporting multiple cursors
+- various indentation settings
+- the type of source file
+
+{% error %}
+Despite all of that sandboxing, Apple has carelessly left open a serious attack vector,
+whereby an unscrupulous extension developer could easily inflict their chosen tab/space fetish on unsuspecting users.
+🙀, indeed.
+{% enderror %}
+
+With text and selections in hand, we get to do the meaty work of the extension.
+Then XcodeKit provides two ways to write back to the same source file,
+by mutating either the `completeBuffer` or the more performant `lines` property.
+Mutating one changes the other,
+and Xcode applies those changes once the completion handler is called.
+Modifying the `selections` property updates the user's selection in the same way.
+
+In our example,
+we first loop over the `lines` of code.
+For each line,
+we use a [regular expression](/swift-regular-expressions/)
+to determine if it has a code mark that needs reformatting.
+If so, we note the index number and the replacement line.
+Finally we mutate the `lines` property to update the source file,
+and call the completion handler to signal that we're done:
+
+```swift
+func perform(with invocation: XCSourceEditorCommandInvocation,
+ completionHandler: @escaping (Error?) -> Void ) -> Void
+{
+ replaceLines(in: invocation.buffer.lines, by: formattingMarks)
+ completionHandler(nil)
+}
+
+func replaceLines(in lines: NSMutableArray,
+ by replacing: @escaping (String) -> String?)
+{
+ guard let strings = lines as? [String] else {
+ return
+ }
+
+ let newStrings: [(Int, String)] = strings.enumerated().compactMap {
+ let (index, line) = $0
+ guard let replacementLine = replacing(line) else {
+ return nil
+ }
+ return (index, replacementLine)
+ }
+
+ newStrings.forEach {
+ let (index, newString) = $0
+ lines[index] = newString
+ }
+}
+
+func formattingMarks(in string: String) -> String? {
+ /* Regex magic transforms:
+ "// fixme here be 🐉"
+ to
+ "// FIXME: here be 🐉"
+ */
+}
+```
+
+{% info %}
+It's worth remembering that extensions can deal with all sorts of text, not just Swift or Objective-C.
+Even files that ordinarily open with specialized viewers in Xcode can instead be viewed as Source Code,
+which makes extension commands available.
+So if we need a transformation of Metal, Markdown, GPX, string dictionaries and the like,
+this is possible via an extension.
+The buffer's `contentUTI` property reports back specific file types,
+which can be interrogated for conformance to more abstract types with [`UTTypeConformsTo`](https://developer.apple.com/documentation/coreservices/1444079-uttypeconformsto).
+{% endinfo %}
+
+## Development Tips
+
+### Debugging
+
+Debugging the extension target launches it in a separate Xcode instance, with a dark status bar and icon:
+
+{% asset "xcode-source-editor-dark-xcode-dock.png" alt="Screenshot showing macOS dock with blue and grey Xcode icons" %}
+
+Sometimes attaching to the debugger fails silently,
+and it's a [good idea](https://ericasadun.com/2016/07/21/explorations-into-the-xcode-source-editor-extensions-underbelly-part-1/)
+to set a log or audible breakpoint to track this:
+
+```swift
+func extensionDidFinishLaunching() {
+ os_log("Extension ready", type: .debug)
+}
+```
+
+#### Extension Scheme Setup
+
+[Two suggestions from Daniel Jalkut](https://academy.realm.io/posts/jalkut-extending-xcode-8/#hot-tips-1532) to make life easier.
+Firstly add Xcode as the default executable in the Extension scheme's Run/Info pane:
+
+{% asset "xcode-source-editor-extension-target-default-executable.png" alt="Screenshot showing Xcode set as default executable in extension scheme" %}
+
+Secondly, add a path to a file or project containing some good code to test against,
+in the Run/Arguments panel of the extension's scheme, under Arguments Passed On Launch:
+
+{% asset "xcode-source-editor-extension-target-launch-arguments.png" alt="Screenshot showing path to sample code under argument passed on launch in extension scheme" %}
+
+#### Testing XcodeKit
+
+Make sure the test target knows how to find the XcodeKit framework,
+if you need to write tests against it.
+Add `${DEVELOPER_FRAMEWORKS_DIR}` as both a Runpath and a Framework Search Path in Build Settings:
+
+{% asset "xcode-source-editor-test-target-build-settings.png" alt="Screenshot showing Developer Frameworks Directory added to Runpath and Framework Search Paths in test target's build settings" %}
+
+### Using `pluginkit`
+
+During development, Xcode can become confused as to which extensions it sees.
+It can be useful to get an overview of installed extensions using the `pluginkit` tool.
+This allows us to query the private PluginKit framework that manages all system extensions.
+
+Here we're matching by the `NSExtensionPointIdentifier` for Source Editor extensions:
+
+```terminal
+$ pluginkit -m -p com.apple.dt.Xcode.extension.source-editor
+
++ com.apple.dt.XCDocumenter.XCDocumenterExtension(1.0)
++ com.apple.dt.XcodeBuiltInExtensions(10.2)
+ com.Swiftify.Xcode.Extension(4.6.1)
++ com.charcoaldesign.SwiftFormat-for-Xcode.SourceEditorExtension(0.40.3)
+! com.hotbeverage.accesscontrolkitty.extension(1.0.1)
+```
+
+The leading flags in the output can give you some clues as to what might be happening:
+
+- `+` [seems to indicate](https://openradar.appspot.com/radar?id=4976827063861248)
+ a specifically enabled extension
+- `-` indicates a disabled extension
+- `!` indicates some form of conflict
+
+For extra verbose output that lists any duplicates:
+
+```terminal
+$ pluginkit -m -p com.apple.dt.Xcode.extension.source-editor -A -D -vvv
+```
+
+If you spot an extension that might be causing an issue, you can try manually removing it:
+
+```terminal
+$ pluginkit -r path/to/extension
+```
+
+Finally, when multiple copies of Xcode are on the same machine, extensions can stop working completely.
+In this case, Apple Developer Relations suggests re-registering your main copy of Xcode with Launch Services
+(it's easiest to temporarily add `lsregister`'s location to `PATH` first):
+
+```terminal
+$ PATH=/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support:"$PATH"
+$ lsregister -f /Applications/Xcode.app
+```
+
+{% info %}
+What are those two `com.apple.dt...` plugins in the output above?
+Well, it seems like the Developer Tools team are using Source Editor extensions inside Xcode itself.
+Looking at the [strings in the binaries](https://github.com/keith/Xcode.app-strings/tree/master/Xcode.app/Contents/PlugIns)
+we get confirmation of information found elsewhere:
+
+`XcodeBuiltInExtensions` handles comment toggling, and
+`XCDocumenterExtension` inserts documentation comments
+(and turns out to be the former Alcatraz plugin
+[VVDocumenter](https://github.com/onevcat/VVDocumenter-Xcode),
+slurped into Xcode).
+Some sort of internal privileging must then happen as they get more appropriate menu locations,
+but the basic mechanism looks the same.
+{% endinfo %}
+
+## Features and Caveats
+
+### Transforming Source Code
+
+Given how limited XcodeKit's text API is, what sorts of things are people making?
+And can it entice tool creators away from the command line?
+(Hint: 😬)
+
+- Linting and style extensions ([reformatting based on rules](https://github.com/nicklockwood/SwiftFormat), wrapping comments, alignment, whitespace adjustment, code organization, [profanity removal](https://itunes.apple.com/us/app/dirtywords-for-xcode/id1447526628?mt=12))
+- Coding helpers (insert and remove caveman [debugging statements](https://github.com/twostraws/Sharpshooter/) at function calls; [moving import statements](https://github.com/markohlebar/Import) up to the top of the file from any location)
+- Translators, from one language to another ([JSON](https://github.com/quicktype/quicktype-xcode) or [Objective-C to Swift](https://swiftify.com))
+- Boilerplate generators ([init statements](https://github.com/Atimca/SwiftInitGenerator), [extraction of a protocol from a class](https://itunes.apple.com/us/app/protocol-for-xcode/id1212245111?mt=12), [coding keys](https://github.com/wleii/TrickerX))
+- Extracting code to put in a different context ([a playground](https://github.com/insidegui/PlayAlways); [a gist](https://github.com/Bunn/Xgist))
+
+All the tools mentioned above are clearly transforming source code in various ways.
+They'll need some information about the structure of that code to do useful work.
+Could they be using SourceKit directly?
+Well, where the extension is on the App Store, we know that they're not.
+**The extension must be sandboxed just to be loaded by Xcode**,
+whereas calls to SourceKit needs to be un-sandboxed,
+which of course won't fly in the App Store.
+We _could_ distribute independently and use an un-sandboxed [XPC service](/inter-process-communication/) embedded in the extension.
+Or more likely, we can write our own single-purpose code to get the job done.
+The power of Xcode's compiler is tantalizingly out of reach here.
+An opportunity, though, if writing a mini-parser sounds like fun
+(🙋🏼,
+and check out [SwiftFormat](https://github.com/nicklockwood/SwiftFormat)'s beautiful lexer implementation for Swift).
+
+### Context-free Source Code
+
+Once we have some way to analyze source code,
+how sophisticated an extension can we then write?
+Let's remember that the current API gives us access to a file of text,
+but not any of its _context_ within a project.
+
+As an example,
+say we want to implement an extension that quickly modifies the access level of Swift code to make it part of a framework's API.
+So an `internal` class's `internal` properties and functions get changed to `public`,
+but `private` or `fileprivate` implementation details are left alone.
+
+We can get most of the way there,
+lexing and parsing the file to figure out where to make appropriate changes,
+taking into account Swift's rules about access inheritance.
+But what happens if one of these transformed methods turns out to have a parameter with an `internal` type?
+If that type is declared in a different file, there's no way for our extension to know,
+and making the method `public` will cause a build error:
+“Method cannot be declared public because its parameter uses an internal type”.
+
+In this example, we're missing type declarations in other files.
+But complex refactorings can need information about how an entire codebase fits together.
+Metadata could also be useful,
+for example,
+what version of Swift the project uses, or a file path to save per-project configuration.
+
+This is a frustrating trade-off for safety.
+While it's feasible to transform the purely _syntactic_ parts of isolated code,
+once any _semantics_ come into play we quickly bump up against that missing context.
+
+### Output
+
+You can only output transformed text back _to the same source file_ using the extension API.
+If you were hoping to generate extensive boilerplate and insert project files automatically,
+this isn't supported and would be fragile to manage via the containing app.
+Anonymous source file in/out sure is secure, but it isn't powerful.
+
+### Heavyweight Architecture; Lightweight Results
+
+Most extensions' containing apps are hollow shells
+with installation instructions and some global preferences.
+Why?
+Well, a Cocoa app _can_ do anything,
+but the extension doesn't give us a lot to work with:
+
+- As creators, we must deal with
+ sandboxed communications to the containing app, the limited API and entitlements.
+ Add complete sandboxing when distributing through the App Store.
+- As users we contend with
+ that convoluted installation experience,
+ and managing preferences for each extension separately in the containing apps.
+
+It's all, effectively, for the privilege of a menu item.
+And the upshot is apparent from a prominent example in the Mac App Store,
+[Swiftify](https://itunes.apple.com/us/story/id1437719440):
+they suggest no fewer than [four superior ways](https://support.swiftify.com/hc/en-us/articles/360000109571-How-would-this-work-with-the-h-and-m-files-Do-you-have-to-convert-code-piece-by-piece-) to access their service,
+over using their own native extension.
+
+## The Handwavy Bit
+
+To further belabor the Xcode-as-castle metaphor,
+Apple has opened the gate just very slightly,
+but also positioned a large gentleman behind it,
+deterring all but the most innocuous of guests.
+
+Extensions might have temporarily pacified the horde,
+but they are no panacea.
+After nearly three years without expanding the API,
+it's no wonder that the App Store is not our destination of choice to augment Xcode.
+And Apple's "best software for most" credo doesn't mean they always get the IDE experience right
+_cough image literals autocompletion cough_,
+or make us optimistic that Xcode will become truly extensible in the style of [VSCode](https://code.visualstudio.com).
+
+But let's swirl some tea leaves and see where Apple _could_ take us
+if they so wished:
+
+- Imagine a world where Xcode is using [SwiftSyntax](/swiftsyntax/) directly to represent the syntax of a file
+ (a [stated goal of the project](https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20170206/004066.html)).
+ Let's imagine that XcodeKit exposes `Syntax` nodes in some way through the extension API.
+ We would be working with _exactly_ the same representation as Xcode —
+ no hand-written parsers needed.
+ [Tools are already being written](https://github.com/apple/swift-syntax#some-example-users) against this library —
+ it would be so neat to get them directly in Xcode.
+- Let's imagine we have specific [read](https://openradar.appspot.com/26823522)/[write access](http://www.openradar.me/35194855) to the current project directory and metadata.
+ Perhaps this leverages the robust entitlements system, with approval through App Review.
+ That sounds good to create extensive boilerplate.
+- Let's expand our vision:
+ there's a way to access fuller semantic information about our code,
+ maybe driven via the [LSP protocol](/language-server-protocol/).
+ Given a better way to output changes too,
+ we could use that information for complex, custom refactorings.
+- Imagine invoking extensions [automatically](http://openradar.appspot.com/27045243),
+ for example as part of the build.
+- Imagine API calls that add custom UI or [Touch Bar items](http://www.openradar.me/29660390), according to context.
+- Imagine a thriving, vibrant section of the [Mac App Store](https://itunes.apple.com/us/story/id1380861178) for developer extensions.
+
+---
+
+_Whew_.
+That magic tea is strong stuff.
+In _that_ world, extensions look a lot more
+fun, powerful, and worth the architectural hassles.
+Of course,
+this is [rank speculation](https://forums.swift.org/t/new-lsp-language-service-supporting-swift-and-c-family-languages-for-any-editor-and-platform/17024/37?u=zoe),
+and yet...
+The open-source projects Apple is committed to working on will --- eventually ---
+change the internal architecture of Xcode,
+and surely stranger things are happening.
+
+For now, though, if any of this potential excites you,
+please write or tweet about it,
+[submit enhancement requests](/bug-reporting/),
+get involved on the
+[relevant](https://forums.swift.org/c/development/sourcekit-lsp)
+[forums](https://forums.swift.org/search?q=swiftsyntax)
+or contribute directly.
+We're still hoping the Xcode team renders this article comprehensively obsolete,
+sooner rather than later 🤞.
diff --git a/2019-04-01-chaetura-vauxi.md b/2019-04-01-chaetura-vauxi.md
new file mode 100644
index 00000000..abb75dc8
--- /dev/null
+++ b/2019-04-01-chaetura-vauxi.md
@@ -0,0 +1,422 @@
+---
+title: Swift
+author: Mattt
+category: Swift
+excerpt: >-
+ Vaux's swifts (_Chaetura vauxi_) is a species of swift
+ native to the American Pacific Northwest and South America.
+ Like others in its genus,
+ vaux's swifts are impressive aerialists,
+ capable of high-precision maneuvers at speeds in excess of 100 km/h.
+ On the other hand, they are often described as
+ _"small, dark, fast-flying cigars with wings"_,
+ which isn't a particularly majestic designation.
+status:
+ swift: n/a
+retired: true
+published: false
+---
+
+> Since the progress of civilization in our country
+> has furnished thousands of convenient places for this Swallow to breed in,
+> safe from storms, snakes, or quadrupeds,
+> it has abandoned, with a judgment worthy of remark,
+> its former abodes in the hollows of trees,
+> and taken possession of the chimneys which emit no smoke in the summer season.
+> For this reason, no doubt,
+> it has obtained the name by which it is generally known.
+>
+> John J Audubon, _Birds of America_, Plate 158: "American Swift"
+
+{% asset chaetura-hipsterus.png width="350" class="chaetura-hipsterus" alt="Chaetura hipsterus" %}
+
+Vaux's swifts (_Chaetura vauxi_) is a species of swift
+native to the American Pacific Northwest and South America.
+Like others in its genus,
+vaux's swifts are impressive aerialists,
+capable of high-precision maneuvers at speeds in excess of 100 km/h.
+On the other hand, they are frequently described as
+_"small, dark, fast-flying cigars with wings"_,
+which isn't a particularly majestic characterization.
+
+In the Alphabet District of Portland Oregon
+_(a short walk from NSHipster headquarters, as it were)_,
+Chapman Elementary School
+is host to North America's largest concentration of Vaux's swifts.
+
+{% info %}
+Appropriately enough,
+Portland is also the home of Chris Lattner's
+[alma mater](upcs).
+{% endinfo %}
+
+Every evening, from late summer through October,
+thousands of swifts can be seen just before sunset
+as they fly into the school's old smokestack to roost for the night.
+At dawn, they emerge once again
+and continue their migration to Central and South America.
+
+Vaux's are among the more gregarious species of swifts,
+observed to flock in the dozens.
+Moving together as a group,
+the whirling mass of birds flying in and out of their roost
+creates a phantasmal silhouette against the twilight sky .
+
+Among the first computer simulations of this flocking behavior
+was a program called [Boids](https://en.wikipedia.org/wiki/Boids),
+created by Craig Reynolds in 1986.
+It remains one of the most striking examples of emergent behavior,
+with complex --- seemingly chaotic --- interactions
+arising from a small set of simple rules:
+
+- **separation**:
+ steer to avoid crowding local flockmates
+- **alignment**:
+ steer towards the average heading of local flockmates
+- **cohesion**:
+ steer to move towards the average position
+ (center of mass) of local flockmates
+
+The [following simulation][flocking]
+is an implementation of Craig Reynold's "Boids" program,
+created by Daniel Shiffman using [Processing.js](http://processingjs.org).
+
+
+
+Click or tap to add a new bird.
+
+{% asset processing.min.js %}
+
+
+
+
+
+
+
+As you gaze upon this computational approximation of flocking swifts,
+consider, for a moment, the emergent nature of your own behavior.
+
+_What separates you from others?
+How often is your direction more a consequence of your surroundings
+than a reasoned, conscious choice?
+And when you are, indeed, making such a decision,
+how is your choice shaped by the consensus of your peers?_
+
+...or don't.
+Such philosophical introspection is but a fool's errand.
+
+[flocking]: http://processingjs.org/learning/topic/flocking/
+[upcs]: https://engineering.up.edu/abet-accredited-undergraduate-programs/computer-science.html
diff --git a/2019-04-08-uitableviewheaderfooterview.md b/2019-04-08-uitableviewheaderfooterview.md
new file mode 100644
index 00000000..18e9c458
--- /dev/null
+++ b/2019-04-08-uitableviewheaderfooterview.md
@@ -0,0 +1,430 @@
+---
+title: UITableViewHeaderFooterView
+author: Mattt
+category: Cocoa
+excerpt: >-
+ Sure, everyone knows that they can (and should!) reuse table view cells.
+ But did you know that you can do the same for section headers and footers?
+status:
+ swift: 5.0
+---
+
+`UITableView` is the bread and butter of iOS apps.
+This is as true today as it was with the first iPhone over a decade ago.
+
+Back in those early days,
+developers worked _hard_ to achieve smooth scroll performance ---
+often resorting to extreme measures.
+For example,
+to achieve 60FPS on a table view with custom cells on an iPhone 3G
+you'd often have to draw text directly to a cell's Core Graphics context,
+because compositing subviews was too slow.
+_(Interface Builder? Auto Layout?
+**Phooey!**
+Back in my day, we calculated all of our view frames by hand ---
+up hill, both ways, in the snow)_
+
+At the time,
+the highest praise a developer could receive for their efforts
+was to have someone describe theirs as "buttery":
+smooth, responsive, without any jitter.
+And we milked that hardware for all it was worth to make that happen.
+
+In honor of all the skeuomorphic sweat spilled
+to transmute code into that most golden of dairy products,
+and in the interest of maximizing the performance of our apps today,
+we'll turn our attention to a class that ---
+for many of us ---
+has been hiding in plain sight:
+`UITableViewHeaderFooterView`.
+
+---
+
+Introduced in iOS 6,
+[`UITableViewHeaderFooterView`](https://developer.apple.com/documentation/uikit/uitableviewheaderfooterview)
+takes the reuse functionality of table view cells
+and makes it available to section headers and footers.
+You can either use it directly
+or create a subclass to customize its appearance.
+
+Now,
+table views are responsible for a great deal of functionality,
+and one can easily lose track of how all of its responsibilities are delegated.
+So let's start with a quick run-down of `UITableView`
+before going into more detail about `UITableViewHeaderFooterView`:
+
+## UITableView Review
+
+A `UITableView` consists of sections,
+each of which containing a number of rows.
+
+For each row,
+the table view's `dataSource` is responsible for returning a `UITableViewCell`
+to represent each section / row index path
+with the `tableView(_:cellForRowAt:)` delegate method.
+The table view's `dataSource` may also provide
+a title to be displayed in the header or footer of a section
+by implementing the optional
+`tableView(_:titleForHeaderInSection:)` and
+`tableView(_:titleForFooterInSection:)` delegate methods.
+
+To customize the appearance of section headers or footers,
+the table view's `delegate` can implement the optional delegate methods
+`tableView(_:viewForHeaderInSection:)` and
+`tableView(_:viewForFooterInSection:)`.
+To keep scroll performance snappy,
+table views recycle their cells as they scroll out of view.
+This process is known as cell reuse.
+You can take advantage of reuse for section headers and footers,
+by returning an instance of `UITableViewHeaderFooterView` (or a subclass).
+
+{% info %}
+Perhaps the main reason why
+so few developers know about `UITableViewHeaderFooterView`
+is that even after its introduction in iOS 6,
+`tableView(_:viewForHeaderInSection:)` and
+`tableView(_:viewForFooterInSection:)`
+continue to return an optional `UIView`.
+Why?
+It's all to do with the iOS platform team's commitment to API compatibility.
+An app written against the iPhone OS 2 SDK should (theoretically)
+still build and run on iOS 12!
+{% endinfo %}
+
+---
+
+What better way to demonstrate this obscure technique for buttery performance
+than to create an app to display per-capita dairy product consumption statistics
+from the USDA?
+_(That was a hypothetical question.)_
+
+{% asset uitableviewheaderfooterview-storyboard.png %}
+
+---
+
+For our example,
+we'll keep our model nice and simple,
+with a nearly 1:1 mapping with the API we'll use to
+display this information on a table view:
+
+```swift
+struct Section {
+ let title: String
+ let image: UIImage
+ let rows: [(year: Int, amount: Decimal)]
+ let notes: String?
+}
+
+let butter = Section(title: "Butter",
+ image: #imageLiteral(resourceName: "Butter"),
+ rows: [ <#...#> ],
+ notes: nil)
+
+// etc.
+
+let sections: [Section] = [
+ milk, yogurt, butter, cheese,
+ cottageCheese, condensedMilk, iceCream, whey
+]
+```
+
+In the view controller itself,
+the implementation for `UITableViewDataSource` delegate methods
+is cool and refreshing:
+
+```swift
+import UIKit
+
+final class ViewController: UIViewController {
+ @IBOutlet var tableView: UITableView!
+}
+
+// MARK: - UITableViewDataSource
+
+extension ViewController: UITableViewDataSource {
+ func numberOfSections(in tableView: UITableView) -> Int {
+ return sections.count
+ }
+
+ func tableView(_ tableView: UITableView,
+ numberOfRowsInSection section: Int) -> Int {
+ return sections[section].rows.count
+ }
+
+ func tableView(_ tableView: UITableView,
+ cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+ let cell = tableView.dequeueReusableCell(withIdentifier: "Cell",
+ for: indexPath)
+
+ let section = sections[indexPath.section]
+ let row = section.rows[indexPath.row]
+
+ cell.textLabel?.text = "\(row.year)"
+ cell.detailTextLabel?.text = "\(row.amount)"
+
+ return cell
+ }
+}
+```
+
+Alright, let's cut the cheese
+and talk about the right wheyway
+to use `UITableViewHeaderFooterView`.
+
+## Creating a Section Header View
+
+In this example,
+we'll offer two different approaches to
+working with `UITableViewHeaderFooterView`.
+
+In the first,
+we'll do everything in code;
+in the second,
+we'll design things visually in Interface Builder.
+Feel free to adopt whichever one you prefer.
+
+### Option 1: Constructing the View Programmatically
+
+Similar to `UITableViewCell`
+each `UITableViewHeaderFooterView` comes with
+`textLabel` and `detailTextLabel` properties
+that are lazily created and positioned within a `contentView`.
+As with cells,
+you have the option to take or leave these built-in subviews
+for your custom subclass.
+
+For our example,
+let's use the existing `textLabel`
+and add an imageView along the trailing margin of the `contentView`.
+We do all of this in the designated initializer,
+`init(reuseIdentifier:)`:
+
+```swift
+import UIKit
+
+final class SectionHeaderView: UITableViewHeaderFooterView {
+ static let reuseIdentifier: String = String(describing: self)
+
+ var imageView: UIImageView
+
+ override init(reuseIdentifier: String?) {
+ super.init(reuseIdentifier: reuseIdentifier)
+
+ imageView = UIImageView()
+ contentView.addSubview(imageView)
+
+ imageView.translatesAutoresizingMaskIntoConstraints = false
+
+ imageView.widthAnchor.constraint(equalToConstant: 24.0).isActive = true
+ imageView.heightAnchor.constraint(equalToConstant: 24.0).isActive = true
+ imageView.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor).isActive = true
+ imageView.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor).isActive = true
+ }
+
+ required init?(coder aDecoder: NSCoder) {
+ super.init(coder: aDecoder)
+ }
+}
+```
+
+In our view controller,
+we register the custom section header in `viewDidLoad()`
+by calling the `register(_:forHeaderFooterViewReuseIdentifier:)` method
+on `tableView`:
+
+```swift
+import UIKit
+
+final class ViewController: UIViewController {
+ @IBOutlet var tableView: UITableView!
+
+ // MARK: UIViewController
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ self.tableView.register(
+ SectionHeaderView.self,
+ forHeaderFooterViewReuseIdentifier:
+ SectionHeaderView.reuseIdentifier
+ )
+ }
+}
+```
+
+### Option 2: Designing the View in Interface Builder
+
+Dynamic table view cells can be designed directly from a Storyboard,
+which can be quite convenient for prototyping interfaces.
+Unfortunately, at the time of writing,
+there is no documented way to design prototype section header / footer views
+as you can with table view cells.
+
+However,
+we can still use Interface Builder to design our section header and footer views ---
+all it takes a few extra steps.
+
+First, create a new Swift file
+that declares your `UITableViewHeaderFooterView` subclass.
+
+Next,
+create a new XIB file for your custom view:
+
+{% asset uitableviewheaderfooterview-new-xib.png %}
+
+In Interface Builder,
+navigate to the Identity Inspector
+in the Inspectors panel on the right-hand side,
+and set your subclass as the "Custom Class" for both
+File's Owner and the top-level view.
+
+{% asset uitableviewheaderfooterview-custom-view.png %}
+
+Back in your subclass implementation,
+declare an `imageView` property
+and an override to the existing `textLabel` property ---
+both with `@IBOutlet` annotations ---
+and connect them to their counterparts in Interface Builder.
+
+```swift
+import UIKit
+
+final class SectionHeaderView: UITableViewHeaderFooterView {
+ static let reuseIdentifier: String = String(describing: self)
+
+ static var nib: UINib {
+ return UINib(nibName: String(describing: self), bundle: nil)
+ }
+
+ // Override `textLabel` to add `@IBOutlet` annotation
+ @IBOutlet override var textLabel: UILabel? {
+ get { return _textLabel }
+ set { _textLabel = newValue }
+ }
+ private var _textLabel: UILabel?
+
+ @IBOutlet var imageView: UIImageView!
+}
+```
+
+{% warning %}
+This step isn't required for using
+`UITableViewHeaderFooterView` with Interface Builder.
+As an alternative to overriding `textLabel`,
+you can declare a new label property to use instead.
+{% endwarning %}
+
+Now, when you register your subclass for reuse with the table view controller,
+pass a `UINib` (provided here in a type property)
+instead of `SectionHeaderView.self`.
+
+```swift
+import UIKit
+
+final class ViewController: UIViewController {
+ @IBOutlet var tableView: UITableView!
+
+ // MARK: UIViewController
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ self.tableView.register(
+ SectionHeaderView.nib,
+ forHeaderFooterViewReuseIdentifier:
+ SectionHeaderView.reuseIdentifier
+ )
+ }
+}
+```
+
+## Implementing UITableViewDelegate Methods
+
+From here,
+it's smooth scrolling sailing.
+Enjoy your victory lap as you implement the requisite
+`UITableViewDelegate` methods:
+
+```swift
+import UIKit
+
+// MARK: - UITableViewDelegate
+
+extension ViewController: UITableViewDelegate {
+ func tableView(_ tableView: UITableView,
+ titleForHeaderInSection section: Int) -> String? {
+ return sections[section].title
+ }
+
+ func tableView(_ tableView: UITableView,
+ titleForFooterInSection section: Int) -> String? {
+ return sections[section].notes
+ }
+
+ func tableView(_ tableView: UITableView,
+ viewForHeaderInSection section: Int) -> UIView? {
+ guard let view = tableView.dequeueReusableHeaderFooterView(
+ withIdentifier: SectionHeaderView.reuseIdentifier)
+ as? SectionHeaderView
+ else {
+ return nil
+ }
+
+ view.textLabel?.text = sections[section].title
+ view.imageView?.image = sections[section].image
+
+ return view
+ }
+
+ <#...#>
+}
+```
+
+### Self-Sizing Header and Footer Views
+
+Using Auto Layout constraints to layout your reusable section views?
+If so,
+you can take advantage of the same self-sizing behavior of table view cells
+to accommodate trait variations and text spanning multiple lines
+in your headers and footers.
+The trick is to specify an automatic height and provide an estimate.
+This can be done in a Storyboard or in the code for your table view delegate:
+
+```swift
+extension ViewController: UITableViewDelegate {
+ <#...#>
+
+ func tableView(_ tableView: UITableView,
+ heightForHeaderInSection section: Int) -> CGFloat {
+ return UITableView.automaticDimension
+ }
+
+ func tableView(_ tableView: UITableView,
+ estimatedHeightForHeaderInSection section: Int) -> CGFloat {
+ return 50.0
+ }
+}
+```
+
+{% info %}
+If you find that your self-sizing section header or footer views
+are bleeding into their adjacent cells,
+make sure to provide an estimated row height as well,
+either in your Storyboard
+or by implementing the `tableView(_:estimatedHeightForRowAt:)` delegate method.
+{% endinfo %}
+
+---
+
+With today's comparatively over-powered iOS hardware,
+such proactive measures may well be unnecessary for achieving
+buttery smooth interactions.
+
+But for those of your with demanding performance requirements,
+for anyone yearning to be in the 2%,
+to achieve the crème de la crème of responsive interfaces,
+`UITableViewHeaderFooterView` can be a great way to skim some fat off your code.
+
+If nothing else,
+its restrained, familiar API allows `UITableViewHeaderFooterView`
+to be added to most codebases without introducing much churn.
diff --git a/2019-04-15-guided-access.md b/2019-04-15-guided-access.md
new file mode 100644
index 00000000..916acf8c
--- /dev/null
+++ b/2019-04-15-guided-access.md
@@ -0,0 +1,430 @@
+---
+title: Guided Access
+author: Mattt
+category: Cocoa
+excerpt: >-
+ Improve your analog device interactions
+ with this one weird accessibility trick.
+status:
+ swift: 5.0
+---
+
+Accessibility features on iOS are more like superpowers
+than assistive technologies.
+Open the Settings app and navigate to General > Accessibility
+and you'll find a treasure trove of functionality,
+capable of feats otherwise impossible on a platform as locked down as iOS.
+
+Want to use your iPhone as a magnifying glass?
+Turn on Magnifier for a convenient way to
+zoom in on small details with your camera.
+
+Care to learn the contents of a webpage
+without as much as glancing at your screen?
+Enable "Speak Screen" or "Speak Selection"
+to have the page [dictated, not read](/avspeechsynthesizer/).
+
+Disagree with the aesthetic direction of iOS
+since Jony Ive took over UI design in version 7?
+With just a few taps,
+you can do away with frivolous transparency and motion effects
+and give buttons the visual distinction they deserve.
+
+But venture down to the bottom of the accessibility settings menu,
+and you'll find arguably the most obscure among these accessibility features:
+Guided Access.
+
+What is it?
+Why is it useful?
+How can you build your app to better support it?
+
+Read on,
+and let NSHipster be your guide.
+
+## What Is Guided Access?
+
+Guided Access
+is an accessibility feature introduced in iOS 6
+that restricts user interactions within an app.
+
+When a Guided Access session is started,
+the user is unable to close the app until the session is ended
+(either by entering a passcode or authenticating with Face ID or Touch ID).
+Additionally,
+a Guided Access session can be configured
+to block interactions with designated screen regions
+and allow or deny any combination of the following features:
+
+- **Sleep / Wake Button**:
+ Prevent the screen and device from being turned off
+- **Volume Buttons**:
+ Disable hardware volume buttons
+- **Motion**:
+ Ignore device rotation and shake gestures
+- **Keyboards**
+ Don't show the keyboard
+- **Touch**
+ Ignore screen touches
+- **Time Limit**
+ Enforce a given time limit for using the app
+
+## Why is Guided Access Useful?
+
+With a name like _"Guided Access"_,
+it's not immediately clear what this feature actually does.
+And its section heading "Learning" doesn't help much, either ---
+though, to be fair, that isn't an _inaccurate_ characterization
+(Guided Access is undoubtedly useful for people with a learning disability),
+but it certainly buries the lede.
+
+In truth, Guided Access can be many things to many different people.
+So for your consideration,
+here are some alternative names that you can keep at the back of your mind
+to better appreciate when and why you might give it a try:
+
+### "Kid-Proof Mode": Sharing Devices with Children
+
+If you have a toddler and want to facilitate a FaceTime call with a relative,
+start a Guided Access session before you pass the device off.
+This will prevent your little one from accidentally hanging up
+or putting the call on hold by switching to a different app.
+
+### "Adult-Proof Mode": Sharing Devices with Other Adults
+
+The next time you go to hand off your phone to someone else to take a photo,
+give it a quick triple-tap to enter Guided Access mode first
+to forego the whole "Oops, I accidentally locked the device" routine.
+
+{% info %}
+Among the interactions disabled by Guided Access is screen recording,
+which makes it a great way to capture interactions during
+in-person user testing sessions.
+{% endinfo %}
+
+### "Crowd-Proof Mode": Displaying a Device in Kiosk Mode
+
+Have a spare iPad that you want to allows guests to sign-in at an event?
+Guided Access offers a quick and effective way to keep things moving.
+
+{% warning %}
+For more robust and extensive control over a device,
+there's [Mobile Device Management](https://developer.apple.com/videos/play/wwdc2018/302/)
+(MDM).
+
+MDM and Guided Access interact in some interesting ways.
+For example,
+education apps can call the
+[requestGuidedAccessSession(enabled:completionHandler:)](https://developer.apple.com/documentation/uikit/uiaccessibility/1615186-requestguidedaccesssession)
+to enter Single App mode while a student takes a test.
+iOS 12.2 extends Guided Access functionality for managed devices,
+although this is [currently undocumented](https://developer.apple.com/documentation/uikit/uiaccessibility/3089195-configureforguidedaccess).
+{% endwarning %}
+
+### "You-Proof Mode": Focus Your Attention on a Device
+
+Guided Access can be helpful even when
+you aren't handing off the device to someone else:
+
+If you're prone to distraction and need to focus on study or work,
+Guided Access can help keep you on track.
+Conversely,
+if you're kicking back and enjoying a game on your phone,
+but find the touch controls to be frustratingly similar to built-in iOS gestures,
+you can use Guided Access to keep you safely within the "magic circle".
+(The same goes for anyone whose work looks more like play,
+such as digital musicians and other performers.)
+
+## Setting Up Guided Access
+
+To set up Guided Access,
+open the Settings app,
+navigate to General > Accessibility > Guided Access,
+and enable the switch labeled Guided Access.
+
+Next, tap Passcode Settings,
+and enter (and reenter) the passcode
+that you'll use to end Guided Access sessions.
+
+## Starting a Guided Access Session
+
+To start a Guided Access session,
+triple-click the home button
+(or, on the iPhone X and similar models, the side button).
+Alternatively, you can start a session by telling Siri
+"Turn on Guided Access".
+
+From here,
+you can trace regions of the screen
+for which user interaction is disabled,
+and configure which of the aforementioned features are allowed.
+
+{% asset guided-access-restrictions.jpg width=400 %}
+
+---
+
+As far as accessibility features are concerned,
+Guided Access is the least demanding for developers:
+Nearly all are compatible as-is, without modification.
+
+However,
+there are ways that you might improve ---
+and even enhance ---
+Guided Access functionality in your app:
+
+---
+
+## Detecting When Guided Access is Enabled
+
+To determine whether the app is running within a Guided Access session,
+access the `isGuidedAccessEnabled` type property
+from the `UIAccessibility` namespace:
+
+```swift
+UIAccessibility.isGuidedAccessEnabled
+```
+
+You can use `NotificationCenter` to subscribe to notifications
+whenever a Guided Access session is started or ended
+by observing for `UIAccessibility.guidedAccessStatusDidChangeNotification`.
+
+```swift
+NotificationCenter.default.addObserver(
+ forName: UIAccessibility.guidedAccessStatusDidChangeNotification,
+ object: nil,
+ queue: .main
+) { (notification) in
+ <#respond to notification#>
+}
+```
+
+All of that said:
+most apps won't really be an actionable response
+to Guided Access sessions starting or ending ---
+at least not unless they extend this functionality
+by adding custom restrictions.
+
+## Adding Custom Guided Access Restrictions to Your App
+
+If your app performs any destructive (i.e. not easily reversible) actions
+that aren't otherwise precluded by any built-in Guided Access restrictions,
+you might consider providing a custom restriction.
+
+To get a sense of what these might entail,
+think back to the previous use-cases for Guided Access and consider:
+_Which functionality would I **might not** want to expose to a toddler / stranger / crowd_?
+Some ideas that quickly come to mind are
+deleting a photo from Camera Roll,
+overwriting game save data,
+or anything involving a financial transaction.
+
+A custom restriction can be enabled or disabled as part of a Guided Access session
+like any of the built-in restrictions.
+However, unlike the ones that come standard in iOS,
+it's the responsibility of your app to determine
+how a restriction behaves.
+
+Let's take a look at what that means in code:
+
+### Defining Custom Guided Access Restrictions
+
+First,
+create an enumeration with a case
+for each restriction that you want your app to support.
+Each restriction needs a unique identifier,
+so it's convenient to make that the `rawValue`.
+Conformance to `CaseIterable` in the declaration here
+automatically synthesizes an `allCases` type property that we'll use later on.
+
+For this example,
+let's define a restriction that, when enabled,
+prevents a user from initiating a purchase within a Guided Access session:
+
+```swift
+enum Restriction: String, CaseIterable {
+ case purchase = "com.nshipster.example.restrictions.purchase"
+}
+```
+
+### Adopting the UIGuidedAccessRestrictionDelegate Protocol
+
+Once you've defined your custom restrictions,
+extend your `AppDelegate` to adopt
+the `UIGuidedAccessRestrictionDelegate` protocol,
+and have it conform by implementing the following methods:
+
+- `guidedAccessRestrictionIdentifiers`
+- `textForGuidedAccessRestriction(withIdentifier:)`
+- `detailTextForGuidedAccessRestriction(withIdentifier:)` _(optional)_
+- `guidedAccessRestriction(withIdentifier:didChange:)`
+
+For `guidedAccessRestrictionIdentifiers`,
+we can simply return a mapping of the `rawValue` for each of the cases.
+For `textForGuidedAccessRestriction(withIdentifier:)`,
+one convenient pattern is to leverage optional chaining
+on a computed `text` property.
+
+```swift
+import UIKit
+
+extension Restriction {
+ var text: String {
+ switch self {
+ case .purchase:
+ return NSLocalizedString("Purchase", comment: "Text for Guided Access purchase restriction")
+ }
+ }
+}
+
+// MARK: - UIGuidedAccessRestrictionDelegate
+
+extension AppDelegate: UIGuidedAccessRestrictionDelegate {
+ var guidedAccessRestrictionIdentifiers: [String]? {
+ return Restriction.allCases.map { $0.rawValue }
+ }
+
+ func textForGuidedAccessRestriction(withIdentifier restrictionIdentifier: String) -> String? {
+ return Restriction(rawValue: restrictionIdentifier)?.text
+ }
+
+ <#...#>
+}
+```
+
+The last protocol method to implement is
+`guidedAccessRestriction(withIdentifier:didChange:)`,
+which notifies our app when access restrictions are turned on and off.
+
+```swift
+ func guidedAccessRestriction(withIdentifier restrictionIdentifier: String, didChange newRestrictionState: UIAccessibility.GuidedAccessRestrictionState) {
+ let notification: Notification
+
+ switch newRestrictionState {
+ case .allow:
+ notification = Notification(name: UIAccessibility.guidedAccessDidAllowRestrictionNotification, object: restrictionIdentifier)
+ case .deny:
+ notification = Notification(name: UIAccessibility.guidedAccessDidDenyRestrictionNotification, object: restrictionIdentifier)
+ @unknown default:
+ // Switch covers known cases,
+ // but 'UIAccessibility.GuidedAccessRestrictionState'
+ // may have additional unknown values,
+ // possibly added in future versions
+ return
+ }
+
+ NotificationCenter.default.post(notification)
+ }
+```
+
+{% info %}
+The `@unknown default` case seen here
+has to do with the new
+[non-frozen enumerations](https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md)
+introduced in Swift 5 for C language interoperability
+(though it's hard to imagine any other future cases beyond `allow` and `deny`...)
+{% endinfo %}
+
+Most of the responsibility falls on each view controller
+in determining how to respond to this kind of change.
+So here, we'll rebroadcast the message as a notification.
+
+The existing `UIAccessibility.guidedAccessStatusDidChangeNotification`
+fires when Guided Access is switched on or off,
+but it's unclear from the documentation what the contract is for
+changing options in a Guided Access session without entirely ending it.
+So to be safe,
+we'll define additional notifications that we can use to respond accordingly:
+
+```swift
+extension UIAccessibility {
+ static let guidedAccessDidAllowRestrictionNotification = NSNotification.Name("com.nshipster.example.notification.allow-restriction")
+
+ static let guidedAccessDidDenyRestrictionNotification = NSNotification.Name("com.nshipster.example.notification.deny-restriction")
+}
+```
+
+### Responding to Changes in Custom Guided Access Restrictions
+
+Finally,
+in our view controllers,
+we'll register for all of the guided access notifications we're interested in,
+and define a convenience method to respond to them.
+
+For example,
+`ProductViewController` has a `puchaseButton` outlet
+that's configured according to the custom `.purchase` restriction
+defined by the app:
+
+```swift
+import UIKit
+
+class ProductViewController: UIViewController {
+ @IBOutlet var purchaseButton: UIButton!
+
+ // MARK: UIViewController
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+ self.updateViewForGuidedAccess()
+ }
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ let selector = #selector(updateViewForGuidedAccess)
+
+ let names: [Notification.Name] = [
+ UIAccessibility.guidedAccessStatusDidChangeNotification,
+ UIAccessibility.guidedAccessDidAllowRestrictionNotification,
+ UIAccessibility.guidedAccessDidDenyRestrictionNotification
+ ]
+
+ for name in names {
+ NotificationCenter.default.addObserver(self, selector: selector, name: name, object: nil)
+ }
+ }
+
+ // MARK: -
+
+ @objc private func updateViewForGuidedAccess() {
+ guard UIAccessibility.isGuidedAccessEnabled else { return }
+
+ switch UIAccessibility.guidedAccessRestrictionState(forIdentifier: Restriction.purchase.rawValue) {
+ case .allow:
+ purchaseButton.isEnabled = true
+ purchaseButton.isHidden = false
+ case .deny:
+ purchaseButton.isEnabled = false
+ purchaseButton.isHidden = true
+ @unknown default:
+ break
+ }
+ }
+}
+```
+
+---
+
+Accessibility is an important issue for developers ---
+especially for mobile and web developers,
+who are responsible for designing and implementing
+the digital interfaces on which we increasingly depend.
+
+Each of us,
+(if we're fortunate to live so long),
+are almost certain to have our ability to
+see or hear diminish over time.
+_"Accessibility is designing for our future selves"_,
+as the popular saying goes.
+
+But perhaps it'd be more accurate to say
+_"Accessibility is designing for our future selves our day-to-day selves"_.
+
+Even if you don't identify as someone who's differently-abled,
+there are frequently situations in which you might be temporarily impaired,
+whether it's trying to
+read in low-light setting
+or listen to someone in a loud environment
+or interact with a device while holding a squirming toddler.
+
+Features like Guided Access offer a profound reminder
+that accessibility features benefit _everyone_.
diff --git a/2019-04-22-cggeometry.md b/2019-04-22-cggeometry.md
new file mode 100644
index 00000000..65de3d80
--- /dev/null
+++ b/2019-04-22-cggeometry.md
@@ -0,0 +1,477 @@
+---
+title: CoreGraphics Geometry Primitives
+author: Mattt
+category: Cocoa
+excerpt: >-
+ Unless you were a Math Geek or an Ancient Greek,
+ Geometry probably wasn't your favorite subject in school.
+ More likely, you were that kid in class
+ who dutifully programmed all of those necessary formulæ
+ into your TI-8X calculator to avoid rote memorization.
+revisions:
+ "2012-12-17": Original publication
+ "2015-02-17": Updated for Swift 2
+ "2019-04-22": Updated for Swift 5
+status:
+ swift: 5.0
+ reviewed: April 22, 2019
+---
+
+Unless you were a Math Geek or an Ancient Greek,
+Geometry probably wasn't your favorite subject in school.
+More likely, you were that kid in class
+who dutifully programmed all of those necessary formulæ
+into your TI-8X calculator to avoid rote memorization.
+
+So for those of you who spent more time learning TI-BASIC than Euclid,
+here's the cheat-sheet for how geometry works in [Quartz 2D][quartz-2d],
+the drawing system used by Apple platforms:
+
+{::nomarkdown}
+
+
+{% asset core-graphics-primitives.svg width="900" %}
+CoreGraphics Primitives (iOS)
+
+{:/}
+
+- A `CGFloat`
+ represents a scalar quantity.
+- A `CGPoint`
+ represents a location in a two-dimensional coordinate system
+ and is defined by `x` and `y` scalar components.
+- A `CGVector`
+ represents a change in position in 2D space
+ and is defined by `dx` and `dy` scalar components.
+- A `CGSize`
+ represents the extent of a figure in 2D space
+ and is defined by `width` and `height` scalar components.
+- A `CGRect`
+ represents a rectangle
+ and is defined by an origin point (`CGPoint`) and a size (`CGSize`).
+
+```swift
+import CoreGraphics
+
+let float: CGFloat = 1.0
+let point = CGPoint(x: 1.0, y: 2.0)
+let vector = CGVector(dx: 4.0, dy: 3.0)
+let size = CGSize(width: 4.0, height: 3.0)
+var rectangle = CGRect(origin: point, size: size)
+```
+
+{% info %}
+`CGVector` isn't widely used in view programming;
+instead, `CGSize` values are typically used to express positional vectors.
+Unfortunately, this can result in awkward semantics,
+because sizes may have negative `width` and / or `height` components
+(in which case a rectangle is extended
+in the opposite direction along that dimension).
+{% endinfo %}
+
+{::nomarkdown}
+
+
+{% asset core-graphics-coordinate-systems.svg width="500" %}
+CoreGraphics Coordinates Systems (iOS)
+
+{:/}
+
+On iOS, the origin is located at the top-left corner of a window,
+so `x` and `y` values increase as they move down and to the right.
+macOS, by default, orients `(0, 0)` at the bottom left corner of a window,
+such that `y` values increase as they move up.
+
+{% info %}
+You can configure views on macOS to use the same coordinate system as iOS
+by overriding the
+[`isFlipped`](https://developer.apple.com/documentation/appkit/nsview/1483532-isflipped) property
+on subclasses of `NSView`.
+{% endinfo %}
+
+---
+
+Every view in an iOS or macOS app
+has a `frame` represented by a `CGRect` value,
+so one would do well to learn the fundamentals
+of these geometric primitives.
+
+In this week's article,
+we'll do a quick run through the APIs
+with which every app developer should be familiar.
+
+---
+
+## Introspection
+
+_"First, know thyself."_
+So goes the philosophical aphorism.
+And it remains practical guidance as we begin our survey of CoreGraphics API.
+
+As structures,
+you can access the member values of geometric types
+directly through their stored properties:
+
+```swift
+point.x // 1.0
+point.y // 2.0
+
+size.width // 4.0
+size.height // 3.0
+
+rectangle.origin // {x 1 y 2}
+rectangle.size // {w 4 h 3}
+```
+
+You can mutate variables by reassignment
+or by using mutating operators like `*=` and `+=`:
+
+```swift
+var mutableRectangle = rectangle // {x 1 y 2 w 4 h 3}
+mutableRectangle.origin.x = 7.0
+mutableRectangle.size.width *= 2.0
+mutableRectangle.size.height += 3.0
+mutableRectangle // {x 7 y 2 w 8 h 6}
+```
+
+For convenience,
+rectangles also expose `width` and `height`
+as top-level, computed properties;
+(`x` and `y` coordinates must be accessed through the intermediary `origin`):
+
+```swift
+rectangle.origin.x
+rectangle.origin.y
+rectangle.width
+rectangle.height
+```
+
+{% info %}
+However, you can't use these convenience accessors
+to change the underlying rectangle like in the preceding example:
+
+```swift
+mutableRectangle.width *= 2.0 // {x 1 y 2 w 8 h 3} // ⚠️ Left side of mutating operator isn't mutable: 'width' is a get-only property
+```
+
+{% endinfo %}
+
+### Accessing Minimum, Median, and Maximum Values
+
+Although a rectangle can be fully described by
+a location (`CGPoint`) and an extent (`CGSize`),
+that's just one side of the story.
+
+For the other 3 sides,
+use the built-in convenience properties
+to get the minimum (`min`), median (`mid`), and maximum (`max`) values
+in the `x` and `y` dimensions:
+
+{::nomarkdown}
+
+
+{% asset core-graphics-cgrect-min-mid-max.svg width="500" %}
+CoreGraphics CGRect Properties (iOS)
+
+{:/}
+
+```swift
+rectangle.minX // 1.0
+rectangle.midX // 3.0
+rectangle.maxX // 5.0
+
+rectangle.minY // 2.0
+rectangle.midY // 3.5
+rectangle.maxY // 5.0
+```
+
+#### Computing the Center of a Rectangle
+
+It's often useful to compute the center point of a rectangle.
+Although this isn't provided by the framework SDK,
+you can easily extend `CGRect` to implement it
+using the `midX` and `midY` properties:
+
+```swift
+extension CGRect {
+ var center: CGPoint {
+ return CGPoint(x: midX, y: midY)
+ }
+}
+```
+
+## Normalization
+
+Things can get a bit strange when you use
+non-integral or negative values in geometric calculations.
+Fortunately,
+CoreGraphics has just the APIs you need
+to keep everything in order.
+
+### Standardizing Rectangles
+
+We expect that a rectangle's origin is situated at its top-left corner.
+However,
+if its size has a negative width or height,
+the origin could become any of the other corners instead.
+
+For example,
+consider the following _bizarro_ rectangle
+that extends leftwards and upwards from its origin.
+
+```swift
+let ǝןƃuɐʇɔǝɹ = CGRect(origin: point,
+ size: CGSize(width: -4.0, height: -3.0))
+ǝןƃuɐʇɔǝɹ // {x 1 y 2 w -4 h -3}
+```
+
+We can use the `standardized` property
+to get the equivalent rectangle
+with non-negative width and height.
+In the case of the previous example,
+the standardized rectangle
+has a width of `4` and height of `3`
+and is situated at the point `(-3, -1)`:
+
+```swift
+ǝןƃuɐʇɔǝɹ.standardized // {x -3 y -1 w 4 h 3}
+```
+
+### Integrating Rectangles
+
+It's generally a good idea for all `CGRect` values
+to be rounded to the nearest whole point.
+Fractional values can cause the frame to be drawn on a pixel boundary.
+Because pixels are atomic units,
+a fractional value causes drawing to be averaged over the neighboring pixels.
+The result: blurry lines that don't look great.
+
+The `integral` property takes the `floor` each origin value
+and the `ceil` each size value.
+This ensures that your drawing code aligns on pixel boundaries crisply.
+
+```swift
+let blurry = CGRect(x: 0.1, y: 0.5, width: 3.3, height: 2.7)
+blurry // {x 0.1 y 0.5 w 3.3 h 2.7}
+blurry.integral // {x 0 y 0 w 4 h 4}
+```
+
+{% info %}
+Though keep in mind that CoreGraphics coordinates
+operate in terms of points not pixels.
+For example,
+a Retina screen with pixel density of 2
+represents each point with 4 pixels
+and can draw `± 0.5f` point values on odd pixels without blurriness.
+{% endinfo %}
+
+## Transformations
+
+While it's possible to mutate a rectangle
+by performing member-wise operations on its origin and size,
+the CoreGraphics framework offers better solutions
+by way of the APIs discussed below.
+
+### Translating Rectangles
+
+Translation describes the geometric operation of
+moving a shape from one location to another.
+
+Use the `offsetBy` method (or `CGRectOffset` function in Objective-C)
+to translate a rectangle's origin by a specified `x` and `y` distance.
+
+```swift
+rectangle.offsetBy(dx: 2.0, dy: 2.0) // {x 3 y 4 w 4 h 3}
+```
+
+Consider using this method whenever you shift a rectangle's position.
+Not only does it save a line of code,
+but it more semantically represents intended operation
+than manipulating the origin values individually.
+
+### Contracting and Expanding Rectangles
+
+Other common transformations for rectangles
+include contraction and expansion around a center point.
+The `insetBy(dx:dy:)` method can accomplish both.
+
+When passed a positive value for either component,
+this method returns a rectangle that
+_shrinks_ by the specified amount from each side
+as computed from the center point.
+For example,
+when inset by `1.0` horizontally (`dy = 0.0`),
+a rectangle originating at `(1, 2)`
+with a width of `4` and `height` equal to `3`,
+produces a new rectangle originating at `(2, 2)`
+with width equal to `2` and height equal to `3`.
+Which is to say:
+**the result of insetting a rectangle by `1` point horizontally
+is a rectangle whose `width` is `2` points _smaller_ than the original.**
+
+```swift
+rectangle // {x 1 y 2 w 4 h 3}
+rectangle.insetBy(dx: 1.0, dy: 0.0) // {x 2 y 2 w 2 h 3}
+```
+
+When passed a negative value for either component,
+the rectangle _grows_ by that amount from each side.
+When passed a non-integral value,
+this method may produce a rectangle with non-integral components.
+
+```swift
+rectangle.insetBy(dx: -1.0, dy: 0.0) // {x 0 y 2 w 6 h 3}
+rectangle.insetBy(dx: 0.5, dy: 0.0) // {x 1.5 y 2 w 3 h 3}
+```
+
+{% info %}
+For more complex transformations,
+another option is [`CGAffineTransform`](https://developer.apple.com/documentation/coregraphics/cgaffinetransform),
+which allows you to translate, scale, and rotate geometries ---
+all at the same time!
+_(We'll cover affine transforms in a future article)_
+{% endinfo %}
+
+## Identities and Special Values
+
+Points, sizes, and rectangles each have a `zero` property,
+which defines the identity value for each respective type:
+
+```swift
+CGPoint.zero // {x 0 y 0}
+CGSize.zero // {w 0 h 0}
+CGRect.zero // {x 0 y 0 w 0 h 0}
+```
+
+Swift shorthand syntax allows you to pass `.zero` directly
+as an argument for methods and initializers,
+such as `CGRect.init(origin:size:)`:
+
+```swift
+let square = CGRect(origin: .zero,
+ size: CGSize(width: 4.0, height: 4.0))
+```
+
+{% info %}
+To determine whether a rectangle is empty (has zero size),
+use the `isEmpty` property
+rather than comparing its `size` to `CGSize.zero`.
+
+```swift
+CGRect.zero.isEmpty // true
+```
+
+{% endinfo %}
+
+---
+
+`CGRect` has two additional special values: `infinite` and `null`:
+
+```swift
+CGRect.infinite // {x -∞ y -∞ w +∞ h +∞}
+CGRect.null // {x +∞ y +∞ w 0 h 0}
+```
+
+`CGRect.null` is conceptually similar to `NSNotFound`,
+in that it represents the absence of an expected value,
+and does so using the largest representable number to exclude all other values.
+
+`CGRect.infinite` has even more interesting properties,
+as it intersects with all points and rectangles,
+contains all rectangles,
+and its union with any rectangle is itself.
+
+```swift
+CGRect.infinite.contains(<#any point#>) // true
+CGRect.infinite.intersects(<#any other rectangle#>) // true
+CGRect.infinite.union(<#any other rectangle#>) // CGRect.infinite
+```
+
+Use `isInfinite` to determine whether a rectangle is, indeed, infinite.
+
+```swift
+CGRect.infinite.isInfinite // true
+```
+
+But to fully appreciate why these values exist and how they're used,
+let's talk about geometric relationships:
+
+## Relationships
+
+Up until this point,
+we've been dealing with geometries in isolation.
+To round out our discussion,
+let's consider what's possible when evaluating two or more rectangles.
+
+### Intersection
+
+Two rectangles intersect if they overlap.
+Their intersection is the smallest rectangle
+that encompasses all points contained by both rectangles.
+
+{::nomarkdown}
+
+
+{% asset core-graphics-intersection.svg width="400" %}
+CoreGraphics CGRect Intersection (iOS)
+
+{:/}
+
+In Swift,
+you can use the `intersects(_:)` and `intersection(_:)` methods
+to efficiently compute the intersection of two `CGRect` values:
+
+```swift
+let square = CGRect(origin: .zero,
+ size: CGSize(width: 4.0, height: 4.0))
+square // {x 0 y 0 w 4 h 4}
+
+rectangle.intersects(square) // true
+rectangle.intersection(square) // {x 1 y 2 w 3 h 2}
+```
+
+If two rectangles _don't_ intersect,
+the `intersection(_:)` method produces `CGRect.null`:
+
+```swift
+rectangle.intersects(.zero) // false
+rectangle.intersection(.zero) // CGRect.null
+```
+
+### Union
+
+The union of two rectangles
+is the smallest rectangle that encompasses all of the points
+contained by either rectangle.
+
+{::nomarkdown}
+
+
+{% asset core-graphics-union.svg width="400" %}
+CoreGraphics CGRect Union (iOS)
+
+{:/}
+
+In Swift,
+the aptly-named `union(_:)` method does just this for two `CGRect` values:
+
+```swift
+rectangle.union(square) // {x 0 y 0 w 5 h 5}
+```
+
+---
+
+So what if you didn't pay attention in Geometry class ---
+this is the real world.
+And in the real world,
+you have `CGGeometry.h`
+and all of the types and functions it provides.
+
+Know it well,
+and you'll be on your way to discovering great new user interfaces in your apps.
+Do a good enough job with that,
+and you may encounter the best arithmetic problem of all:
+adding up all the money you've made with your awesome new app.
+_Mathematical!_
+
+[quartz-2d]: https://developer.apple.com/library/mac/#documentation/graphicsimaging/Conceptual/drawingwithquartz2d/Introduction/Introduction.html
diff --git a/2019-04-29-optional-throws-result-async-await.md b/2019-04-29-optional-throws-result-async-await.md
new file mode 100644
index 00000000..d0647d76
--- /dev/null
+++ b/2019-04-29-optional-throws-result-async-await.md
@@ -0,0 +1,515 @@
+---
+title: Optional, throws, Result, async/await
+author: jemmons
+category: Swift
+excerpt: "An exploration of error handling in Swift: then, now, and soon."
+status:
+ swift: 5.0
+---
+
+Back in the early days of Swift 1, we didn't have much in the way of error handling.
+But we _did_ have `Optional`,
+and it felt awesome!
+By making null checks explicit and enforced,
+bombing out of a function by returning `nil` suddenly felt less like a code smell
+and more like a language feature.
+
+Here's how we might write a little utility to grab Keychain data returning `nil` for any errors:
+
+```swift
+func keychainData(service: String) -> Data? {
+ let query: NSDictionary = [
+ kSecClass: kSecClassGenericPassword,
+ kSecAttrService: service,
+ kSecReturnData: true
+ ]
+ var ref: CFTypeRef? = nil
+
+ switch SecItemCopyMatching(query, &ref) {
+ case errSecSuccess:
+ return ref as? Data
+ default:
+ return nil
+ }
+}
+```
+
+We set up a query,
+pass an an empty `inout` reference to `SecItemCopyMatching` and then,
+depending on the status code we get back,
+either return the reference as data
+or `nil` if there was an error.
+
+At the call site,
+we can tell if something has exploded by unwrapping the optional:
+
+```swift
+if let myData = keychainData(service: "My Service") {
+ <#do something with myData...#>
+} else {
+ fatalError("Something went wrong with... something?")
+}
+```
+
+## Getting Results
+
+There's a certain binary elegance to the above,
+but it conceals an achilles heel.
+[At its heart](https://github.com/apple/swift/blob/swift-5.0-RELEASE/stdlib/public/core/Optional.swift#L122),
+`Optional` is just an enum that holds either some wrapped value or nothing:
+
+```swift
+enum Optional {
+ case some(Wrapped)
+ case none
+}
+```
+
+This works just fine for our utility when everything goes right — we just return our value.
+But most operations that involve I/O can go wrong
+(`SecItemCopyMatching`, in particular, can go wrong [many, many ways](https://developer.apple.com/documentation/security/1542001-security_framework_result_codes)),
+and `Optional` ties our hands when it comes to signaling something's gone sideways.
+Our only option is to return nothing.
+
+Meanwhile, at the call site, we're wondering what the issue is and all we've got to work with is this empty `.none`.
+It's difficult to write robust software when every non-optimal condition is essentially reduced to `¯\_(ツ)_/¯`.
+How could we improve this situation?
+
+One way would be to add some language-level features that let functions throw errors in addition to returning values.
+And this is, in fact, exactly what Swift did in version 2 with its `throws/throw` and `do/catch` syntax.
+
+But let's stick with our `Optional` line of reasoning for just a moment. If the issue is that `Optional` can only hold a value or `nil`, and in the event of an error `nil` isn't expressive enough, maybe we can address the issue simply by making a new `Optional` that holds either a value _or an error?_
+
+Well, congratulations are in order:
+change a few names, and we see we just invented the new `Result` type,
+[now available in the Swift 5 standard library](https://github.com/apple/swift/blob/swift-5.0-RELEASE/stdlib/public/core/Result.swift#L16)!
+
+```swift
+enum Result {
+ case success(Success)
+ case failure(Failure)
+}
+```
+
+`Result` holds either a successful value _or_ an error.
+And we can use it to improve our little keychain utility.
+
+First,
+let's define a custom `Error` type with some more descriptive cases than a simple `nil`:
+
+```swift
+enum KeychainError: Error {
+ case notData
+ case notFound(name: String)
+ case ioWentBad
+ <#...#>
+}
+```
+
+Next we change our `keychainData` definition to return `Result` instead of `Data?`.
+When everything goes right we return our data as the [associated value](https://docs.swift.org/swift-book/LanguageGuide/Enumerations.html#ID148) of a `.success`.
+What happens if any of `SecItemCopyMatching`'s many and varied disasters strike?
+Rather than returning `nil` we return one of our specific errors wrapped in a `.failure`:
+
+```swift
+func keychainData(service: String) -> Result {
+ let query: NSDictionary = [...]
+ var ref: CFTypeRef? = nil
+
+ switch SecItemCopyMatching(query, &ref) {
+ case errSecSuccess:
+ guard let data = ref as? Data else {
+ return .failure(KeychainError.notData)
+ }
+ return .success(data)
+ case errSecItemNotFound:
+ return .failure(KeychainError.notFound(name: service))
+ case errSecIO:
+ return .failure(KeychainError.ioWentBad)
+ <#...#>
+ }
+}
+
+```
+
+Now we have a lot more information to work with at the call site! We can, if we choose, `switch` over the result, handling both success _and_ each error case individually:
+
+```swift
+switch keychainData(service: "My Service") {
+case .success(let data):
+ <#do something with data...#>
+case .failure(KeychainError.notFound(let name)):
+ print("\(name) not found in keychain.")
+case .failure(KeychainError.ioWentBad):
+ print("Error reading from the keychain.")
+case .failure(KeychainError.notData):
+ print("Keychain is broken.")
+<#...#>
+}
+```
+
+All things considered, `Result` seems like a pretty useful upgrade to `Optional`. How on earth did it take it five years to be added to the standard library?
+
+## Three's a Crowd
+
+Alas, `Result` is _also_ cursed with an achilles heel — we just haven't noticed it yet because, up until now, we've only been working with a single call to a single function. But imagine we add two more error-prone operations to our list of `Result`-returning utilities:
+
+```swift
+func makeAvatar(from user: Data) -> Result {
+ <#Return avatar made from user's initials...#>
+ <#or return failure...#>
+}
+
+func save(image: UIImage) -> Result {
+ <#Save image and return success...#>
+ <#or returns failure...#>
+}
+```
+
+{% info %}
+Note the return type of `save(image:)` — its success type is defined as `Void`.
+We don't always have to return a value with our successes.
+Sometimes just knowing it succeeded is enough.
+{% endinfo %}
+
+In our example,
+the first function generates an avatar from user data,
+and the second writes an image to disk.
+The implementations don't matter so much for our purposes,
+just that they return a `Result` type.
+
+Now, how would we write something that fetches user data from the keychain, uses it to create an avatar, saves that avatar to disk, _and_ handles any errors that might occur along the way?
+
+We might try something like:
+
+```swift
+switch keychainData(service: "UserData") {
+case .success(let userData):
+
+ switch makeAvatar(from: userData) {
+ case .success(let avatar):
+
+ switch save(image: avatar) {
+ case .success:
+ break // continue on with our program...
+
+ case .failure(FileSystemError.readOnly):
+ print("Can't write to disk.")
+
+ <#...#>
+ }
+
+ case .failure(AvatarError.invalidUserFormat):
+ print("Unable to generate avatar from given user.")
+
+ <#...#>
+ }
+
+case .failure(KeychainError.notFound(let name)):
+ print(""\(name)" not found in keychain.")
+
+<#...#>
+}
+```
+
+But whooo boy. Adding just two functions has led to an explosion of nesting, dislocated error handling, and woe.
+
+## Falling Flat
+
+Thankfully, we can clean this up by taking advantage of the fact that,
+like `Optional`,
+[`Result` implements `flatMap`](https://github.com/apple/swift/blob/swift-5.0-RELEASE/stdlib/public/core/Result.swift#L96).
+Specifically, `flatMap` on a `Result` will,
+in the case of `.success`,
+apply the given transform to the associated value and return the newly produced `Result`.
+In the case of a `.failure`, however,
+`flatMap` simply passes the `.failure` and its associated error along without modification.
+
+{% info %}
+Things that implement `flatMap` like this are [sometimes called "monads"](http://chris.eidhof.nl/post/monads-in-swift/).
+This can be a useful sobriquet to know as monads all share some common properties.
+But if the term is unfamiliar or scary, don't sweat it.
+`flatMap` is the only thing we need to understand, here.
+{% endinfo %}
+{% warning %}
+There's a common misconception that `flatMap` has been replaced by `compactMap` as of Swift 4.1.
+Not so!
+[Only the specific case of calling `flatMap` on a `Sequence` with `Optional` elements is deprecated.](https://github.com/apple/swift-evolution/blob/master/proposals/0187-introduce-filtermap.md#motivation)
+{% endwarning %}
+
+Because it passes errors through in this manner, we can use `flatMap` to combine our operations together without checking for `.failure` each step of the way. This lets us minimize nesting and keep our error handling and operations distinct:
+
+```swift
+let result = keychainData(service: "UserData")
+ .flatMap(makeAvatar)
+ .flatMap(save)
+
+switch result {
+case .success:
+ break // continue on with our program...
+
+case .failure(KeychainError.notFound(let name)):
+ print(""\(name)" not found in keychain.")
+case .failure(AvatarError.invalidUserFormat):
+ print("Unable to generate avatar from given user.")
+case .failure(FileSystemError.readOnly):
+ print("Can't write to disk.")
+<#...#>
+}
+```
+
+This is, without a doubt, an improvement. But it requires us (and anyone reading our code) to be familiar enough with `.flatMap` to follow its somewhat unintuitive semantics.
+
+{% warning %} And this is a best case scenario of perfect composability (the resulting value of the first operation being the required parameter of the next, and so on). What if an operation takes no parameters? Or requires more than one? Or takes a parameter of a different type than we're returning? `flatMap`ing across those sorts of beasts is… less elegant. {% endwarning %}
+
+Compare this to the `do/catch` syntax from all the way back in Swift 2 that we alluded to a little earlier:
+
+```swift
+do {
+ let userData = try keychainData(service: "UserData")
+ let avatar = try makeAvatar(from: userData)
+ try save(image: avatar)
+
+} catch KeychainError.notFound(let name) {
+ print(""\(name)" not found in keychain.")
+
+} catch AvatarError.invalidUserFormat {
+ print("Not enough memory to create avatar.")
+
+} catch FileSystemError.readOnly {
+ print("Could not save avatar to read-only media.")
+} <#...#>
+```
+
+The first thing that might stand out is how similar these two pieces of code are. They both have a section up top for executing our operations. And both have a section down below for matching errors and handling them.
+
+{% info %} This similarity is not accidental. Much of Swift's error handling [is sugar around returning and unwrapping `Result`-like types](https://twitter.com/jckarter/status/608137115545669632). As we'll see more of in a bit… {% endinfo %}
+
+Whereas the `Result` version has us piping operations through chained calls to `flatMap`,
+we write the `do/catch` code more or less exactly as we would if no error handling were involved.
+While the `Result` version requires we understand the internals of its enumeration
+and explicitly `switch` over it to match errors,
+the `do/catch` version lets us focus on the part we actually care about:
+the errors themselves.
+
+By having language-level syntax for error handling, Swift effectively masks all the `Result`-related complexities it took us the first half of this post to digest: enumerations, associated values, generics, flatMap, monads… In some ways, Swift added error-handling syntax back in version 2 specifically so we wouldn't have to deal with `Result` and its eccentricities.
+
+Yet here we are, five years later, learning all about it. Why add it now?
+
+## Error's Ups and Downs
+
+Well, as it should happen, `do/catch` has this little thing we might call an achilles heel…
+
+See, `throw`, like `return`, only works in one direction; up. We can `throw` an error "up" to the _caller_, but we can't `throw` an error "down" as a parameter to another function _we_ call.
+
+This "up"-only behavior is typically what we want.
+Our keychain utility,
+rewritten once again with error handling,
+is all `return`s and `throw`s because its only job is passing either our data or an error
+back up to the thing that called it:
+
+```swift
+func keychainData(service: String) throws -> Data {
+ let query: NSDictionary = [...]
+ var ref: CFTypeRef? = nil
+
+ switch SecItemCopyMatching(query, &ref) {
+ case errSecSuccess:
+ guard let data = ref as? Data else {
+ throw KeychainError.notData
+ }
+ return data
+ case errSecItemNotFound:
+ throw KeychainError.notFound(name: service)
+ case errSecIO:
+ throw KeychainError.ioWentBad
+ <#...#>
+ }
+}
+```
+
+But what if,
+instead of fetching user data from the keychain,
+we want to get it from a cloud service?
+Even on a fast, reliable connection,
+loading data over a network can take a long time compared to reading it from disk.
+We don't want to block the rest of our application while we wait, of course,
+so we'll make it asynchronous.
+
+But that means we're no longer returning _anything_ "up".
+Instead we're calling "down" into a closure on completion:
+
+```swift
+func userData(for userID: String, completion: (Data) -> Void) {
+ <#get data from the network#>
+ // Then, sometime later:
+ completion(myData)
+}
+```
+
+Now network operations can fail with [all sorts of different errors](https://developer.apple.com/documentation/foundation/urlerror),
+but we can't `throw` them "down" into `completion`.
+So the next best option is to pass any errors along as a second (optional) parameter:
+
+```swift
+func userData(for userID: String, completion: (Data?, Error?) -> Void) {
+ <# Fetch data over the network... #>
+ guard myError == nil else {
+ completion(nil, myError)
+ }
+ completion(myData, nil)
+}
+```
+
+But now the caller, in an effort to make sense of this cartesian maze of possible parameters, has to account for many impossible scenarios in addition to the ones we actually care about:
+
+```swift
+userData(for: "jemmons") { maybeData, maybeError in
+ switch (maybeData, maybeError) {
+ case let (data?, nil):
+ <#do something with data...#>
+ case (nil, URLError.timedOut?):
+ print("Connection timed out.")
+ case (nil, nil):
+ fatalError("🤔Hmm. This should never happen.")
+ case (_?, _?):
+ fatalError("😱What would this even mean?")
+ <#...#>
+ }
+}
+```
+
+It'd be really helpful if, instead of this mishmash of "data or nil _and_ error or nil" we had some succinct way to express simply "data _or_ error".
+
+## Stop Me If You've Heard This One…
+
+Wait, data or error?
+That sounds familiar.
+What if we used a `Result`?
+
+```swift
+func userData(for userID: String, completion: (Result) -> Void) {
+ // Everything went well:
+ completion(.success(myData))
+
+ // Something went wrong:
+ completion(.failure(myError))
+}
+```
+
+And at the call site:
+
+```swift
+userData(for: "jemmons") { result in
+ switch (result) {
+ case (.success(let data)):
+ <#do something with data...#>
+ case (.failure(URLError.timedOut)):
+ print("Connection timed out.")
+ <#...#>
+}
+```
+
+Ah ha!
+So we see that the `Result` type can serve as a concrete [reification](https://en.wikipedia.org/wiki/Reification_%28computer_science%29) of Swift's abstract idea of
+_"that thing that's returned when a function is marked as `throws`."_
+And as such, we can use it to deal with asynchronous operations that require concrete types for parameters passed to their completion handlers.
+
+{% info %}
+This duality between
+the abstract "error handling thing"
+and concrete "`Result` thing"
+is more than just skin deep — they're two sides of the same coin,
+as illustrated by how trivial it is to convert between them:
+
+```swift
+Result { try somethingThatThrows() }
+```
+
+…turns an abstract catchable thing into a concrete result type that can be passed around.
+
+```swift
+try someResult.get()
+```
+
+…turns a concrete result into an abstract thing capable of being caught.
+{% endinfo %}
+
+So, while the shape of `Result` has been implied by error handling since Swift 2
+(and, indeed, quite a few developers have created [their own versions of it](https://github.com/search?o=desc&q=result+language%3Aswift&s=&type=Repositories) in the intervening years),
+it's now officially added to the standard library in Swift 5 — primarily as a way to deal with asynchronous errors.
+
+Which is undoubtedly better than passing the double-optional `(Value?, Error?)` mess we saw earlier.
+But didn't we just get finished making the case that `Result` tended to be overly verbose, nesty, and complex
+when dealing with more than one error-capable call?
+Yes we did.
+
+And, in fact, this is even more of an issue in the async space
+since `flatMap` expects its transform to return _synchronously_.
+So we can't use it to compose _asynchronous_ operations:
+
+```swift
+userData(for: "jemmons") { userResult in
+ switch userResult {
+ case .success(let user):
+ fetchAvatar(for: user) { avatarResult in
+
+ switch avatarResult {
+ case .success(let avatar):
+ cloudSave(image: avatar) { saveResult in
+
+ switch saveResult {
+ case .success:
+ // All done!
+
+ case .failure(URLError.timedOut)
+ print("Operation timed out.")
+ <#...#>
+ }
+ }
+
+ case .failure(AvatarError.invalidUserFormat):
+ print("User not recognized.")
+ <#...#>
+ }
+ }
+
+ case .failure(URLError.notConnectedToInternet):
+ print("No internet detected.")
+ <#...#>
+}
+```
+
+{% info %} There is, actually, a `flatMap`-like way of handling this called the [Continuation Monad](https://en.wikipedia.org/wiki/Monad_%28functional_programming%29#Continuation_monad). It's complicated enough, though, that it probably warrants a few blog posts all unto itself. {% endinfo %}
+
+## Awaiting the Future
+
+In the near term, we just have to lump it.
+It's better than the other alternatives native to the language,
+and chaining asynchronous calls isn't as common as for synchronous calls.
+
+But in the future, just as Swift used `do/catch` syntax to define away `Result` nesting problems in synchronous error handling, there are many proposals being considered to do the same for asynchronous errors (and asynchronous processing, generally).
+
+[The async/await proposal](https://gist.github.com/lattner/429b9070918248274f25b714dcfc7619) is one such animal. If adopted it would reduce the above to:
+
+```swift
+do {
+ let user = try await userData(for: "jemmons")
+ let avatar = try await fetchAvatar(for: user)
+ try await cloudSave(image: avatar)
+
+} catch AvatarError.invalidUserFormat {
+ print("User not recognized.")
+
+} catch URLError.timedOut {
+ print("Operation timed out.")
+
+} catch URLError.notConnectedToInternet {
+ print("No internet detected.")
+} <#...#>
+```
+
+Which, holy moley! As much as I love `Result`, I, for one, cannot wait for it to be made completely irrelevant by our glorious async/await overlords.
+
+Meanwhile? Let us rejoice!
+For we finally have a concrete `Result` type in the standard library to light the way through these, the middle ages of async error handling in Swift.
diff --git a/2019-05-06-image-resizing.md b/2019-05-06-image-resizing.md
new file mode 100644
index 00000000..a79616f6
--- /dev/null
+++ b/2019-05-06-image-resizing.md
@@ -0,0 +1,541 @@
+---
+title: Image Resizing Techniques
+author: Mattt
+category: Miscellaneous
+excerpt: "Since time immemorial, iOS developers have been perplexed by a singular question: 'How do you resize an image?' This article endeavors to provide a clear answer to this eternal question."
+status:
+ swift: 5.0
+ reviewed: May 6, 2019
+revisions:
+ "2014-09-15": Original publication
+ "2015-09-30": Updated for Swift 2.0
+ "2019-05-06": Updated for Swift 5.0
+---
+
+Since time immemorial, iOS developers have been perplexed by a singular question:
+
+_"How do you resize an image?"_
+
+It's a question of beguiling clarity,
+spurred on by a mutual mistrust of developer and platform.
+Myriad code samples litter Stack Overflow,
+each claiming to be the One True Solution™ ---
+all others, mere pretenders.
+
+In this week's article,
+we'll look at 5 distinct techniques to image resizing on iOS
+(and macOS, making the appropriate `UIImage` → `NSImage` conversions).
+But rather than prescribe a single approach for every situation,
+we'll weigh ergonomics against performance benchmarks
+to better understand when to use one approach over another.
+
+{% info %}
+
+You can try out each of these image resizing techniques for yourself
+by downloading, building, and running
+[this sample code project](https://github.com/NSHipster/Image-Resizing-Example).
+
+{% endinfo %}
+
+---
+
+## When and Why to Scale Images
+
+Before we get too far ahead of ourselves,
+let's establish _why_ you'd need to resize images in the first place.
+After all,
+`UIImageView` automatically scales and crops images
+according to the behavior specified by its
+[`contentMode` property](https://developer.apple.com/documentation/uikit/uiview/1622619-contentmode).
+And in the vast majority of cases,
+`.scaleAspectFit`, `.scaleAspectFill`, or `.scaleToFill`
+provides exactly the behavior you need.
+
+```swift
+imageView.contentMode = .scaleAspectFit
+imageView.image = image
+```
+
+---
+
+So when does it make sense to resize an image?
+**When it's significantly larger than the image view that's displaying it.**
+
+---
+
+Consider [this stunning image of the Earth](https://visibleearth.nasa.gov/view.php?id=78314),
+from [NASA's Visible Earth image catalog](https://visibleearth.nasa.gov):
+
+{% asset image-resizing-earth.jpg %}
+
+At its full resolution,
+this image measures 12,000 px square
+and weighs in at a whopping 20 MB.
+You might not think much of a few megabytes given today's hardware,
+but that's just its compressed size.
+To display it,
+a `UIImageView` needs to first decode that JPEG into a bitmap.
+If you were to set this full-sized image on an image view as-is,
+your app's memory usage would balloon to
+**hundreds of Megabytes of memory**
+with no appreciable benefit to the user
+(a screen can only display so many pixels, after all).
+
+By simply resizing that image to the size of the image view
+before setting its `image` property,
+you can use an order-of-magnitude less RAM:
+
+| | Memory Usage _(MB)_ |
+| -------------------- | ------------------- |
+| Without Downsampling | 220.2 |
+| With Downsampling | 23.7 |
+
+This technique is known as downsampling,
+and can significantly improve the performance of your app
+in these kinds of situations.
+If you're interested in some more information about downsampling
+and other image and graphics best practices,
+please refer to
+[this excellent session from WWDC 2018](https://developer.apple.com/videos/play/wwdc2018/219/).
+
+Now,
+few apps would ever try to load an image this large...
+but it's not _too_ far off from some of the assets I've gotten back from designer.
+_(Seriously, a 3MB PNG for a color gradient?)_
+So with that in mind,
+let's take a look at the various ways that you can go about
+resizing and downsampling images.
+
+{% error %}
+
+This should go without saying,
+but all of the examples loading images from a URL are for **local** files.
+Remember, it's **never** a good idea to do networking
+synchronously on the main thread of your app.
+
+{% enderror %}
+
+---
+
+## Image Resizing Techniques
+
+There are a number of different approaches to resizing an image,
+each with different capabilities and performance characteristics.
+And the examples we're looking at in this article
+span frameworks both low- and high-level,
+from Core Graphics, vImage, and Image I/O
+to Core Image and UIKit:
+
+1. [Drawing to a UIGraphicsImageRenderer](#technique-1-drawing-to-a-uigraphicsimagerenderer)
+2. [Drawing to a Core Graphics Context](#technique-2-drawing-to-a-core-graphics-context)
+3. [Creating a Thumbnail with Image I/O](#technique-3-creating-a-thumbnail-with-image-io)
+4. [Lanczos Resampling with Core Image](#technique-4-lanczos-resampling-with-core-image)
+5. [Image Scaling with vImage](#technique-5-image-scaling-with-vimage)
+
+For consistency,
+each of the following techniques share a common interface:
+
+```swift
+func resizedImage(at url: URL, for size: CGSize) -> UIImage? { <#...#> }
+
+imageView.image = resizedImage(at: url, for: size)
+```
+
+Here, `size` is a measure of point size,
+rather than pixel size.
+To calculate the equivalent pixel size for your resized image,
+scale the size of your image view frame by the `scale` of your main `UIScreen`:
+
+```swift
+let scaleFactor = UIScreen.main.scale
+let scale = CGAffineTransform(scaleX: scaleFactor, y: scaleFactor)
+let size = imageView.bounds.size.applying(scale)
+```
+
+{% info %}
+If you're loading a large image asynchronously,
+use a transition to have the image fade-in when set on the image view.
+For example:
+
+```swift
+class ViewController: UIViewController {
+ @IBOutlet var imageView: UIImageView!
+
+ override func viewWillAppear(_ animated: Bool) {
+ super.viewWillAppear(animated)
+
+ let url = Bundle.main.url(forResource: "Blue Marble West",
+ withExtension: "tiff")!
+
+ DispatchQueue.global(qos: .userInitiated).async {
+ let image = resizedImage(at: url, for: self.imageView.bounds.size)
+
+ DispatchQueue.main.sync {
+ UIView.transition(with: self.imageView,
+ duration: 1.0,
+ options: [.curveEaseOut, .transitionCrossDissolve],
+ animations: {
+ self.imageView.image = image
+ })
+ }
+ }
+ }
+}
+```
+
+{% endinfo %}
+
+---
+
+### Technique #1: Drawing to a UIGraphicsImageRenderer
+
+The highest-level APIs for image resizing are found in the UIKit framework.
+Given a `UIImage`,
+you can draw into a `UIGraphicsImageRenderer` context
+to render a scaled-down version of that image:
+
+```swift
+import UIKit
+
+// Technique #1
+func resizedImage(at url: URL, for size: CGSize) -> UIImage? {
+ guard let image = UIImage(contentsOfFile: url.path) else {
+ return nil
+ }
+
+ let renderer = UIGraphicsImageRenderer(size: size)
+ return renderer.image { (context) in
+ image.draw(in: CGRect(origin: .zero, size: size))
+ }
+}
+```
+
+[`UIGraphicsImageRenderer`](https://developer.apple.com/documentation/uikit/uigraphicsimagerenderer)
+is a relatively new API,
+introduced in iOS 10 to replace the older,
+`UIGraphicsBeginImageContextWithOptions` / `UIGraphicsEndImageContext` APIs.
+You construct a `UIGraphicsImageRenderer` by specifying a point `size`.
+The `image` method takes a closure argument
+and returns a bitmap that results from executing the passed closure.
+In this case,
+the result is the original image scaled down to draw within the specified bounds.
+
+{% info %}
+
+It's often useful to scale the original size
+to fit within a frame without changing the original aspect ratio.
+[`AVMakeRect(aspectRatio:insideRect:)`](https://developer.apple.com/documentation/avfoundation/1390116-avmakerect)
+is a handy function found in the AVFoundation framework
+that takes care of that calculation for you:
+
+```swift
+import func AVFoundation.AVMakeRect
+let rect = AVMakeRect(aspectRatio: image.size, insideRect: imageView.bounds)
+```
+
+{% endinfo %}
+
+### Technique #2: Drawing to a Core Graphics Context
+
+Core Graphics / Quartz 2D
+offers a lower-level set of APIs
+that allow for more advanced configuration.
+
+Given a `CGImage`,
+a temporary bitmap context is used to render the scaled image,
+using the `draw(_:in:)` method:
+
+```swift
+import UIKit
+import CoreGraphics
+
+// Technique #2
+func resizedImage(at url: URL, for size: CGSize) -> UIImage? {
+ guard let imageSource = CGImageSourceCreateWithURL(url as NSURL, nil),
+ let image = CGImageSourceCreateImageAtIndex(imageSource, 0, nil)
+ else {
+ return nil
+ }
+
+ let context = CGContext(data: nil,
+ width: Int(size.width),
+ height: Int(size.height),
+ bitsPerComponent: image.bitsPerComponent,
+ bytesPerRow: 0,
+ space: image.colorSpace ?? CGColorSpace(name: CGColorSpace.sRGB)!,
+ bitmapInfo: image.bitmapInfo.rawValue)
+ context?.interpolationQuality = .high
+ context?.draw(image, in: CGRect(origin: .zero, size: size))
+
+ guard let scaledImage = context?.makeImage() else { return nil }
+
+ return UIImage(cgImage: scaledImage)
+}
+```
+
+This `CGContext` initializer takes several arguments to construct a context,
+including the desired dimensions and
+the amount of memory for each channel within a given color space.
+In this example,
+these parameters are fetched from the `CGImage` object.
+Next, setting the `interpolationQuality` property to `.high`
+instructs the context to interpolate pixels at a 👌 level of fidelity.
+The `draw(_:in:)` method
+draws the image at a given size and position, a
+allowing for the image to be cropped on a particular edge
+or to fit a set of image features, such as faces.
+Finally,
+the `makeImage()` method captures the information from the context
+and renders it to a `CGImage` value
+(which is then used to construct a `UIImage` object).
+
+### Technique #3: Creating a Thumbnail with Image I/O
+
+Image I/O is a powerful (albeit lesser-known) framework for working with images.
+Independent of Core Graphics,
+it can read and write between many different formats,
+access photo metadata,
+and perform common image processing operations.
+The framework offers the fastest image encoders and decoders on the platform,
+with advanced caching mechanisms ---
+and even the ability to load images incrementally.
+
+The important
+`CGImageSourceCreateThumbnailAtIndex` offers a concise API with different options than found in equivalent Core Graphics calls:
+
+```swift
+import ImageIO
+
+// Technique #3
+func resizedImage(at url: URL, for size: CGSize) -> UIImage? {
+ let options: [CFString: Any] = [
+ kCGImageSourceCreateThumbnailFromImageIfAbsent: true,
+ kCGImageSourceCreateThumbnailWithTransform: true,
+ kCGImageSourceShouldCacheImmediately: true,
+ kCGImageSourceThumbnailMaxPixelSize: max(size.width, size.height)
+ ]
+
+ guard let imageSource = CGImageSourceCreateWithURL(url as NSURL, nil),
+ let image = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options as CFDictionary)
+ else {
+ return nil
+ }
+
+ return UIImage(cgImage: image)
+}
+```
+
+Given a `CGImageSource` and set of options,
+the `CGImageSourceCreateThumbnailAtIndex(_:_:_:)` function
+creates a thumbnail of an image.
+Resizing is accomplished by the `kCGImageSourceThumbnailMaxPixelSize` option,
+which specifies the maximum dimension
+used to scale the image at its original aspect ratio.
+By setting either the
+`kCGImageSourceCreateThumbnailFromImageIfAbsent` or
+`kCGImageSourceCreateThumbnailFromImageAlways` option,
+Image I/O automatically caches the scaled result for subsequent calls.
+
+### Technique #4: Lanczos Resampling with Core Image
+
+Core Image provides built-in
+[Lanczos resampling](https://en.wikipedia.org/wiki/Lanczos_resampling) functionality
+by way of the eponymous `CILanczosScaleTransform` filter.
+Although arguably a higher-level API than UIKit,
+the pervasive use of key-value coding in Core Image makes it unwieldy.
+
+That said, at least the pattern is consistent.
+
+The process of
+creating a transform filter,
+configuring it, and
+rendering an output image
+is no different from any other Core Image workflow:
+
+```swift
+import UIKit
+import CoreImage
+
+let sharedContext = CIContext(options: [.useSoftwareRenderer : false])
+
+// Technique #4
+func resizedImage(at url: URL, scale: CGFloat, aspectRatio: CGFloat) -> UIImage? {
+ guard let image = CIImage(contentsOf: url) else {
+ return nil
+ }
+
+ let filter = CIFilter(name: "CILanczosScaleTransform")
+ filter?.setValue(image, forKey: kCIInputImageKey)
+ filter?.setValue(scale, forKey: kCIInputScaleKey)
+ filter?.setValue(aspectRatio, forKey: kCIInputAspectRatioKey)
+
+ guard let outputCIImage = filter?.outputImage,
+ let outputCGImage = sharedContext.createCGImage(outputCIImage,
+ from: outputCIImage.extent)
+ else {
+ return nil
+ }
+
+ return UIImage(cgImage: outputCGImage)
+}
+```
+
+The Core Image filter named `CILanczosScaleTransform`
+accepts an `inputImage`, an `inputScale`, and an `inputAspectRatio` parameter,
+each of which are pretty self-explanatory.
+
+More interestingly,
+a `CIContext` is used here to create a `UIImage`
+(by way of a `CGImageRef` intermediary representation),
+since `UIImage(CIImage:)` doesn't often work as expected.
+Creating a `CIContext` is an expensive operation,
+so a cached context is used for repeated resizing.
+
+{% info %}
+
+A `CIContext` can be created using either the GPU or the CPU (much slower) for rendering.
+Specify the `.useSoftwareRenderer` the option in the initializer to choose which one to use.
+_(Hint: Use the faster one, maybe?)_
+
+{% endinfo %}
+
+### Technique #5: Image Scaling with vImage
+
+Last up,
+it's the venerable [Accelerate framework](https://developer.apple.com/documentation/accelerate) ---
+or more specifically,
+the `vImage` image-processing sub-framework.
+
+vImage comes with a
+[bevy of different functions](https://developer.apple.com/documentation/accelerate/vimage/vimage_operations/image_scaling)
+for scaling an image buffer.
+These lower-level APIs promise high performance with low power consumption,
+but at the cost of managing the buffers yourself
+(not to mention, signficantly more code to write):
+
+```swift
+import UIKit
+import Accelerate.vImage
+
+// Technique #5
+func resizedImage(at url: URL, for size: CGSize) -> UIImage? {
+ // Decode the source image
+ guard let imageSource = CGImageSourceCreateWithURL(url as NSURL, nil),
+ let image = CGImageSourceCreateImageAtIndex(imageSource, 0, nil),
+ let properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as? [CFString: Any],
+ let imageWidth = properties[kCGImagePropertyPixelWidth] as? vImagePixelCount,
+ let imageHeight = properties[kCGImagePropertyPixelHeight] as? vImagePixelCount
+ else {
+ return nil
+ }
+
+ // Define the image format
+ var format = vImage_CGImageFormat(bitsPerComponent: 8,
+ bitsPerPixel: 32,
+ colorSpace: nil,
+ bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.first.rawValue),
+ version: 0,
+ decode: nil,
+ renderingIntent: .defaultIntent)
+
+ var error: vImage_Error
+
+ // Create and initialize the source buffer
+ var sourceBuffer = vImage_Buffer()
+ defer { sourceBuffer.data.deallocate() }
+ error = vImageBuffer_InitWithCGImage(&sourceBuffer,
+ &format,
+ nil,
+ image,
+ vImage_Flags(kvImageNoFlags))
+ guard error == kvImageNoError else { return nil }
+
+ // Create and initialize the destination buffer
+ var destinationBuffer = vImage_Buffer()
+ error = vImageBuffer_Init(&destinationBuffer,
+ vImagePixelCount(size.height),
+ vImagePixelCount(size.width),
+ format.bitsPerPixel,
+ vImage_Flags(kvImageNoFlags))
+ guard error == kvImageNoError else { return nil }
+
+ // Scale the image
+ error = vImageScale_ARGB8888(&sourceBuffer,
+ &destinationBuffer,
+ nil,
+ vImage_Flags(kvImageHighQualityResampling))
+ guard error == kvImageNoError else { return nil }
+
+ // Create a CGImage from the destination buffer
+ guard let resizedImage =
+ vImageCreateCGImageFromBuffer(&destinationBuffer,
+ &format,
+ nil,
+ nil,
+ vImage_Flags(kvImageNoAllocate),
+ &error)?.takeRetainedValue(),
+ error == kvImageNoError
+ else {
+ return nil
+ }
+
+ return UIImage(cgImage: resizedImage)
+}
+```
+
+The Accelerate APIs used here clearly operate at a much lower-level
+than any of the other resizing methods discussed so far.
+But get past the unfriendly-looking type and function names,
+and you'll find that this approach is rather straightforward.
+
+- First, create a source buffer from your input image,
+- Then, create a destination buffer to hold the scaled image
+- Next, scale the image data in the source buffer to the destination buffer,
+- Finally, create an image from the resulting image data in the destination buffer.
+
+---
+
+## Performance Benchmarks
+
+So how do these various approaches stack up to one another?
+
+Here are the results of some [performance benchmarks](/benchmarking/)
+performed on an iPhone 7 running iOS 12.2,
+in [this project](https://github.com/NSHipster/Image-Resizing-Example).
+
+{% asset image-resizing-app-screenshot.png width="325" %}
+
+The following numbers show the average runtime across multiple iterations
+for loading, scaling, and displaying that
+[jumbo-sized picture of the earth](https://visibleearth.nasa.gov/view.php?id=78314)
+from before:
+
+| | Time _(seconds)_ |
+| ------------------------------------------ | ---------------- |
+| Technique #1: `UIKit` | 0.1420 |
+| Technique #2: `Core Graphics` 1 | 0.1722 |
+| Technique #3: `Image I/O` | 0.1616 |
+| Technique #4: `Core Image` 2 | 2.4983 |
+| Technique #5: `vImage` | 2.3126 |
+
+1
+Results were consistent across different values of `CGInterpolationQuality`, with negligible differences in performance benchmarks.
+
+2
+Setting `kCIContextUseSoftwareRenderer` to `true` on the options passed on `CIContext` creation yielded results an order of magnitude slower than base results.
+
+## Conclusions
+
+- **UIKit**, **Core Graphics**, and **Image I/O**
+ all perform well for scaling operations on most images.
+ If you had to choose one (on iOS, at least),
+ `UIGraphicsImageRenderer` is typically your best bet.
+- **Core Image** is outperformed for image scaling operations.
+ In fact,
+ according to Apple's
+ [_Performance Best Practices section of the Core Image Programming Guide_](https://developer.apple.com/library/mac/documentation/graphicsimaging/Conceptual/CoreImaging/ci_performance/ci_performance.html#//apple_ref/doc/uid/TP30001185-CH10-SW1),
+ you should use Core Graphics or Image I/O functions
+ to crop and downsampling images instead of Core Image.
+- Unless you're already working with **`vImage`**,
+ the extra work necessary to use the low-level Accelerate APIs
+ probably isn't justified in most circumstances.
diff --git a/2019-05-20-swift-format.md b/2019-05-20-swift-format.md
new file mode 100644
index 00000000..f445cc0d
--- /dev/null
+++ b/2019-05-20-swift-format.md
@@ -0,0 +1,878 @@
+---
+title: Swift Code Formatters
+author: Mattt
+category: Swift
+excerpt: >-
+ Lately,
+ the Swift community has been buzzing about the latest pitch
+ to adopt an official style guide and formatting tool for the language.
+ Let's take a look at where we're at today,
+ how the proposed `swift-format` tool stacks up,
+ and what we might expect in the future.
+status:
+ swift: 5.0
+revisions:
+ "2019-03-04": Original publication
+ "2019-05-20": Updated and expanded
+---
+
+> I just left a hipster coffee shop.
+> It was packed with iOS devs,
+> whispering amongst each other about how
+> they can't wait for Apple to release
+> an official style guide and formatter for Swift.
+
+Lately,
+the community has been buzzing about
+the proposal from
+[Tony Allevato](https://github.com/allevato) and
+[Dave Abrahams](https://github.com/dabrahams)
+to adopt an official style guide and formatting tool for the Swift language.
+
+Hundreds of community members have weighed in on the
+[initial pitch](https://forums.swift.org/t/pitch-an-official-style-guide-and-formatter-for-swift/21025)
+and [proposal](https://forums.swift.org/t/se-0250-swift-code-style-guidelines-and-formatter/21795/39).
+As with all matters of style,
+opinions are strong, and everybody has one.
+Fortunately,
+the discourse from the community
+has been generally constructive and insightful,
+articulating a diversity of viewpoints, use cases, and concerns.
+
+Since our article was first published back in March,
+the proposal,
+["SE-0250: Swift Code Style Guidelines and Formatter"](https://github.com/apple/swift-evolution/blob/master/proposals/0250-swift-style-guide-and-formatter.md)
+started formal review;
+that process was later
+[suspended](https://forums.swift.org/t/se-0250-swift-code-style-guidelines-and-formatter/21795/217),
+to be reconsidered sometime in the future.
+
+In spite of this,
+Swift code formatting remains a topic of interest to many developers.
+So this week on NSHipster,
+we're taking another look at the current field of
+Swift formatters available today ---
+including the `swift-format` tool released as part of the proposal ---
+and see how they all stack up.
+From there,
+we'll take a step back and try to put everything in perspective.
+
+But first,
+let's start with a question:
+
+---
+
+## What is Code Formatting?
+
+For our purposes,
+we'll define code formatting
+as any change made to code that makes it easier to understand
+without changing its behavior.
+Although this definition extends to differences in equivalent forms,
+(e.g. `[Int]` vs. `Array`),
+we'll limit our discussion here to whitespace and punctuation.
+
+Swift, like many other programming languages,
+is quite liberal in its acceptance of newlines, tabs, and spaces.
+Most whitespace is insignificant,
+having no effect on the code around from the compiler's point of view.
+
+When we use whitespace to make code more comprehensible
+without changing its behavior,
+that's an example of
+[secondary notation](https://en.wikipedia.org/wiki/Secondary_notation);
+the primary notation, of course,
+being the code itself.
+
+{% info %}
+Another example of secondary notation is syntax highlighting,
+discussed in [a previous NSHipster article](/swiftsyntax/).
+{% endinfo %}
+
+Put enough semicolons in the right places,
+and you can write pretty much anything in a single line of code.
+But all things being equal,
+why not use horizontal and vertical whitespace
+to visually structure code in a way that's easier for us to understand,
+right?
+
+Unfortunately,
+the ambiguity created by the compiler's accepting nature of whitespace
+can often cause confusion and disagreement among programmers:
+_"Should I add a newline before a curly bracket?
+How do I break up statements that extend beyond the width of the editor?"_
+
+Organizations often codify guidelines for how to deal with these issues,
+but they're often under-specified, under-enforced, and out-of-date.
+The role of a code formatter
+is to automatically enforce a set of conventions
+so that programmers can set aside their differences
+and get to work solving actual problems.
+
+## Formatter Tool Comparison
+
+The Swift community has considered questions of style from the very beginning.
+Style guides have existed from the very first days of Swift,
+as have various open source tools to automate the process
+of formatting code to match them.
+
+To get a sense of the current state of Swift code formatters,
+we'll take a look at the following four tools:
+
+| Project | Repository URL |
+| ----------------------------------------------------------------------- | --------------------------------------------- |
+| [SwiftFormat](#swiftformat) | |
+| [SwiftLint](#swiftlint) | |
+| [swift-format](#swift-format) | |
+
+{% info %}
+
+For brevity,
+this article discusses only some of the Swift formatting tools available.
+Here are some other ones that you may want to check out:
+[Swimat](https://github.com/Jintin/Swimat),
+[SwiftRewriter](https://github.com/inamiy/SwiftRewriter),
+[swiftfmt](https://github.com/kishikawakatsumi/swiftfmt),
+and [Prettier with Swift Plugin](https://github.com/prettier/prettier).
+
+{% endinfo %}
+
+To establish a basis of comparison,
+we've contrived the following code sample to evaluate each tool
+(using their default configuration):
+
+```swift
+
+ struct ShippingAddress : Codable {
+ var recipient: String
+ var streetAddress : String
+ var locality :String
+ var region :String;var postalCode:String
+ var country:String
+
+ init( recipient: String, streetAddress: String,
+locality: String,region: String,postalCode: String,country:String )
+{
+ self.recipient = recipient
+ self.streetAddress = streetAddress
+ self.locality = locality
+ self.region = region;self.postalCode=postalCode
+ guard country.count == 2, country == country.uppercased() else { fatalError("invalid country code") }
+ self.country=country}}
+
+let applePark = ShippingAddress(recipient:"Apple, Inc.", streetAddress:"1 Apple Park Way", locality:"Cupertino", region:"CA", postalCode:"95014", country:"US")
+
+```
+
+Although code formatting encompasses a wide range of possible
+syntactic and semantic transformations,
+we'll focus on newlines and indentation,
+which we believe to be baseline requirements for any code formatter.
+
+{% info %}
+The performance benchmarks in this article are, admittedly, not very rigorous.
+But they should provide some indication of general performance characteristics.
+Timings are measured in seconds
+and taken on a 2017 MacBook Pro
+with 2.9 GHz Intel Core i7 processor and 16 GB 2133 MHz LPDDR3 memory.
+{% endinfo %}
+
+### SwiftFormat
+
+First up is
+[SwiftFormat](https://github.com/nicklockwood/SwiftFormat),
+a tool as helpful as it is self-descriptive.
+
+#### Installation
+
+SwiftFormat is distributed via [Homebrew](/homebrew/)
+as well as [Mint](https://github.com/yonaskolb/Mint) and
+[CocoaPods](/CocoaPods/).
+
+You can install it by running the following command:
+
+```terminal
+$ brew install swiftformat
+```
+
+In addition,
+SwiftFormat also provides an Xcode Source Editor Extension,
+found in the [EditorExtension](https://github.com/nicklockwood/SwiftFormat/tree/master/EditorExtension),
+which you can use to reformat code in Xcode.
+Or, if you're a user of [VSCode](/vscode/),
+you can invoke SwiftFormat with
+[this plugin](https://marketplace.visualstudio.com/items?itemName=vknabel.vscode-swiftformat).
+
+#### Usage
+
+The `swiftformat` command formats each Swift file
+found in the specified file and directory paths.
+
+```terminal
+$ swiftformat Example.swift
+```
+
+SwiftFormat has a variety of rules that can be configured
+either individually via command-line options
+or using a configuration file.
+
+#### Example Output
+
+Running the `swiftformat` command on our example
+using the default set of rules produces the following result:
+
+```swift
+// swiftformat version 0.40.8
+
+struct ShippingAddress: Codable {
+ var recipient: String
+ var streetAddress: String
+ var locality: String
+ var region: String; var postalCode: String
+ var country: String
+
+ init(recipient: String, streetAddress: String,
+ locality: String, region: String, postalCode: String, country: String) {
+ self.recipient = recipient
+ self.streetAddress = streetAddress
+ self.locality = locality
+ self.region = region; self.postalCode = postalCode
+ guard country.count == 2, country == country.uppercased() else { fatalError("invalid country code") }
+ self.country = country
+ }
+}
+
+let applePark = ShippingAddress(recipient: "Apple, Inc.", streetAddress: "1 Apple Park Way", locality: "Cupertino", region: "CA", postalCode: "95014", country: "US")
+```
+
+As you can see,
+this is a clear improvement over the original.
+Each line is indented according to its scope,
+and each declaration has consistent spacing between punctuation.
+Both the semicolon in the property declarations
+and the newline in the initializer parameters are preserved;
+however, the closing curly braces aren't moved to separate lines
+as might be expected
+this is [fixed in 0.39.5](https://twitter.com/nicklockwood/status/1103595525792845825).
+Great work, [Nick](https://github.com/nicklockwood)!
+
+{% warning %}
+In version 0.38.0 and later,
+`swiftformat` emits a warning if no Swift version is specified.
+You can specify a version either by
+passing the `--swiftversion` command line option or
+adding a `.swift-version` file to the current directory.
+{% endwarning %}
+
+#### Performance
+
+SwiftFormat is consistently the fastest of the tools tested in this article,
+completing in a few milliseconds.
+
+```terminal
+$ time swiftformat Example.swift
+ 0.03 real 0.01 user 0.01 sys
+```
+
+### SwiftLint
+
+Next up is,
+[SwiftLint](https://github.com/realm/SwiftLint),
+a mainstay of the Swift open source community.
+With over 100 built-in rules,
+SwiftLint can perform a wide variety of checks on your code ---
+everything from preferring `AnyObject` over `class` for class-only protocols
+to the so-called "Yoda condition rule",
+which prescribes variables to be placed on
+the left-hand side of comparison operators
+(that is, `if n == 42` not `if 42 == n`).
+
+As its name implies,
+SwiftLint is not primarily a code formatter;
+it's really a diagnostic tool for identifying
+convention violation and API misuse.
+However, by virtue of its auto-correction faculties,
+it's frequently used to format code.
+
+#### Installation
+
+You can install SwiftLint using Homebrew
+with the following command:
+
+```terminal
+$ brew install swiftlint
+```
+
+Alternatively, you can install SwiftLint with
+[CocoaPods](/CocoaPods/),
+[Mint](https://github.com/yonaskolb/Mint),
+or as a [standalone installer package (`.pkg`)](https://github.com/realm/SwiftLint/releases/tag/0.31.0).
+
+#### Usage
+
+To use SwiftLint as a code formatter,
+run the `autocorrect` subcommand
+passing the `--format` option
+and the files or directories to correct.
+
+```terminal
+$ swiftlint autocorrect --format --path Example.swift
+```
+
+#### Example Output
+
+Running the previous command on our example
+yields the following:
+
+```swift
+// swiftlint version 0.32.0
+struct ShippingAddress: Codable {
+ var recipient: String
+ var streetAddress: String
+ var locality: String
+ var region: String;var postalCode: String
+ var country: String
+
+ init( recipient: String, streetAddress: String,
+ locality: String, region: String, postalCode: String, country: String ) {
+ self.recipient = recipient
+ self.streetAddress = streetAddress
+ self.locality = locality
+ self.region = region;self.postalCode=postalCode
+ guard country.count == 2, country == country.uppercased() else { fatalError("invalid country code") }
+ self.country=country}}
+
+let applePark = ShippingAddress(recipient: "Apple, Inc.", streetAddress: "1 Apple Park Way", locality: "Cupertino", region: "CA", postalCode: "95014", country: "US")
+
+```
+
+SwiftLint cleans up the worst of the indentation and inter-spacing issues
+but leaves other, extraneous whitespace intact
+(though it does strip the file's leading newline, which is nice).
+Again, it's worth noting that formatting isn't SwiftLint's primary calling;
+if anything, it's merely incidental to providing actionable code diagnostics.
+And taken from the perspective of _"first, do no harm"_,
+it's hard to complain about the results here.
+
+#### Performance
+
+For everything that SwiftLint checks for,
+it's remarkably snappy ---
+completing in a fraction of a second for our example.
+
+```terminal
+$ time swiftlint autocorrect --quiet --format --path Example.swift
+ 0.09 real 0.04 user 0.01 sys
+```
+
+---
+
+### swift-format
+
+Having looked at the current landscape of available Swift formatters,
+we now have a reasonable baseline for evaluating the `swift-format` tool
+proposed by Tony Allevato and Dave Abrahams.
+
+#### Installation
+
+You can install using [Homebrew](https://brew.sh) with the following command:
+```terminal
+$ brew install swift-format
+```
+
+Alternatively, you can clone its [source repository](https://github.com/apple/swift-format) and build it yourself.
+https://github.com/apple/swift-format.
+
+#### Usage
+
+Run the `swift-format` command,
+passing one or more file and directory paths
+to Swift files that you want to format.
+
+```terminal
+$ swift-format Example.swift
+```
+
+The `swift-format` command also takes a `--configuration` option,
+which takes a path to a JSON file.
+For now,
+the easiest way to customize `swift-format` behavior
+is to dump the default configuration to a file
+and go from there.
+
+```terminal
+$ swift-format -m dump-configuration > .swift-format.json
+```
+
+Running the command above populates the specified file
+with the following JSON:
+
+```json
+{
+ "blankLineBetweenMembers": {
+ "ignoreSingleLineProperties": true
+ },
+ "indentation": {
+ "spaces": 2
+ },
+ "lineBreakBeforeControlFlowKeywords": false,
+ "lineBreakBeforeEachArgument": true,
+ "lineLength": 100,
+ "maximumBlankLines": 1,
+ "respectsExistingLineBreaks": true,
+ "rules": {
+ "AllPublicDeclarationsHaveDocumentation": true,
+ "AlwaysUseLowerCamelCase": true,
+ "AmbiguousTrailingClosureOverload": true,
+ "AvoidInitializersForLiterals": true,
+ "BeginDocumentationCommentWithOneLineSummary": true,
+ "BlankLineBetweenMembers": true,
+ "CaseIndentLevelEqualsSwitch": true,
+ "DoNotUseSemicolons": true,
+ "DontRepeatTypeInStaticProperties": true,
+ "FullyIndirectEnum": true,
+ "GroupNumericLiterals": true,
+ "IdentifiersMustBeASCII": true,
+ "MultiLineTrailingCommas": true,
+ "NeverForceUnwrap": true,
+ "NeverUseForceTry": true,
+ "NeverUseImplicitlyUnwrappedOptionals": true,
+ "NoAccessLevelOnExtensionDeclaration": true,
+ "NoBlockComments": true,
+ "NoCasesWithOnlyFallthrough": true,
+ "NoEmptyAssociatedValues": true,
+ "NoEmptyTrailingClosureParentheses": true,
+ "NoLabelsInCasePatterns": true,
+ "NoLeadingUnderscores": true,
+ "NoParensAroundConditions": true,
+ "NoVoidReturnOnFunctionSignature": true,
+ "OneCasePerLine": true,
+ "OneVariableDeclarationPerLine": true,
+ "OnlyOneTrailingClosureArgument": true,
+ "OrderedImports": true,
+ "ReturnVoidInsteadOfEmptyTuple": true,
+ "UseEnumForNamespacing": true,
+ "UseLetInEveryBoundCaseVariable": true,
+ "UseOnlyUTF8": true,
+ "UseShorthandTypeNames": true,
+ "UseSingleLinePropertyGetter": true,
+ "UseSpecialEscapeSequences": true,
+ "UseSynthesizedInitializer": true,
+ "UseTripleSlashForDocumentationComments": true,
+ "ValidateDocumentationComments": true
+ },
+ "tabWidth": 8,
+ "version": 1
+}
+```
+
+After fiddling with the configuration ---
+such as setting `lineLength` to the correct value of 80 _(don't @ me)_ ---
+you can apply it thusly:
+
+```terminal
+$ swift-format Example.swift --configuration .swift-format.json
+```
+
+#### Example Output
+
+Using its default configuration,
+here's how `swift-format` formats our example:
+
+```swift
+// swift-format 0.0.1 (2019-05-15, 115870c)
+struct ShippingAddress: Codable {
+ var recipient: String
+ var streetAddress: String
+ var locality: String
+ var region: String;
+ var postalCode: String
+ var country: String
+
+ init(
+ recipient: String,
+ streetAddress: String,
+ locality: String,
+ region: String,
+ postalCode: String,
+ country: String
+ ) {
+ self.recipient = recipient
+ self.streetAddress = streetAddress
+ self.locality = locality
+ self.region = region
+ self.postalCode = postalCode
+ guard country.count == 2, country == country.uppercased() else {
+ fatalError("invalid country code")
+ }
+ self.country = country
+ }
+}
+
+let applePark = ShippingAddress(
+ recipient: "Apple, Inc.",
+ streetAddress: "1 Apple Park Way",
+ locality: "Cupertino",
+ region: "CA",
+ postalCode: "95014",
+ country: "US"
+)
+
+```
+
+_Be still my heart!_ 😍
+We could do without the original semicolon,
+but overall, this is pretty unobjectionable ---
+which is exactly what you'd want from an official code style tool.
+
+
+
+### Flexible Output
+
+But in order to fully appreciate the elegance of `swift-format`'s output,
+we must compare it across a multitude of different column widths.
+
+Let's see how it handles this new code sample,
+replete with cumbersome `UIApplicationDelegate` methods
+and `URLSession` construction:
+
+{% info %}
+**Hint**:
+Try resizing the container below
+by clicking and dragging the control at the bottom right corner
+_(this feature is only available in [supported browsers](https://caniuse.com/#feat=css-resize))_.
+{% endinfo %}
+
+
+
+#### 90 Columns
+
+```swift
+import UIKit
+
+@UIApplicationMain
+class AppDelegate: UIResponder, UIApplicationDelegate {
+ var window: UIWindow?
+
+ func application(
+ _ application: UIApplication,
+ didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
+ ) -> Bool {
+
+ let url = URL(string: "https://nshipster.com/swift-format")!
+ URLSession.shared.dataTask(
+ with: url,
+ completionHandler: { (data, response, error) in
+ guard error == nil, let data = data, let response = response as? HTTPURLResponse,
+ (200..<300).contains(response.statusCode) else {
+ fatalError(error?.localizedDescription ?? "Unknown error")
+ }
+
+ if let html = String(data: data, encoding: .utf8) {
+ print(html)
+ }
+ }
+ ).resume()
+
+ // Override point for customization after application launch.
+ return true
+ }
+}
+```
+
+
+
+
+This kind of flexibility isn't particularly helpful in engineering contexts,
+where developers can and should make full use of their screen real estate.
+But for those of us who do technical writing
+and have to wrestle with things like mobile viewports and page margins,
+this is a killer feature.
+
+#### Performance
+
+In terms of performance,
+`swift-format` isn't so fast as to feel instantaneous,
+but not so slow as to be an issue.
+
+```terminal
+$ time swift-format Example.swift
+ 0.24 real 0.16 user 0.14 sys
+```
+
+{% info %}
+Since our initial analysis,
+In Swift 5.1,
+[SwiftSyntax](https://nshipster.com/swiftsyntax/)
+(the parser used by `swift-format`)
+has been updated with significantly improved performance,
+as described in
+[this forum post](https://forums.swift.org/t/speeding-up-swiftsyntax-by-using-the-parser-directly/18493).
+
+Our initial benchmarks reflected the Swift 4.2 version of SwiftSyntax,
+which predate these optimizations.
+Using the latest Swift 5.1 Snapshot
+(2019-05-09, LLVM 59470d46d5, Swift 6d7f3f61d9),
+we didn't see a significant performance improvement from before,
+but we're eager to re-evaluate these results
+with future releases of `swift-format` and Swift 5.1.
+{% endinfo %}
+
+---
+
+## Conclusion: You Don't Need To Wait to Start Using a Code Formatting Tool
+
+Deciding which conventions we want to adopt as a community
+is an important conversation to have,
+worthy of the thoughtful and serious consideration we give
+to any proposed change to the language.
+However,
+the question of whether there should be official style guidelines
+or an authoritative code formatting tool
+shouldn't stop you from taking steps today for your own projects!
+
+We're strongly of the opinion that
+**most projects would be improved by the adoption of a code formatting tool**,
+provided that it meets the following criteria:
+
+- It's stable
+- It's fast (enough)
+- It produces reasonable output
+
+And based on our survey of the tools currently available,
+we can confidently say that
+[SwiftFormat](https://github.com/nicklockwood/SwiftFormat)
+and
+[`swift-format`](https://github.com/google/swift/tree/format)
+both meet these criteria,
+and are suitable for use in production.
+
+_(If we had to choose between the two,
+we'd probably go with `swift-format` on aesthetic grounds.
+But each developer has different preferences
+and each team has different needs,
+and you may prefer something else.)_
+
+While you're evaluating tools to incorporate into your workflow,
+you'd do well to try out
+[SwiftLint](https://github.com/realm/swiftlint),
+if you haven't already.
+In its linting capacity,
+SwiftLint can go a long way to systematically improving code quality ---
+especially for projects that are
+older and larger and have a large number of contributors.
+
+---
+
+The trouble with the debate about code style is that its large and subjective.
+By adopting these tools in our day-to-day workflows today,
+we not only benefit from better, more maintainable code today,
+but we can help move the debate forward,
+from vague feelings to precise observations about any gaps that still remain.
+
+{% asset 'articles/swift-format.js' defer="defer" %}
+{% asset 'articles/swift-format.css' %}
diff --git a/2019-06-12-wwdc-2019.md b/2019-06-12-wwdc-2019.md
new file mode 100644
index 00000000..4c258592
--- /dev/null
+++ b/2019-06-12-wwdc-2019.md
@@ -0,0 +1,435 @@
+---
+title: WWDC 2019
+author: Mattt
+category: Miscellaneous
+excerpt: >-
+ A look back at a mind-blowing week for Apple platform developers,
+ and a look forward at the future of software development.
+status:
+ swift: 5.🤔
+---
+
+Apple's [press release](https://www.apple.com/newsroom/2019/03/apple-to-host-annual-worldwide-developers-conference-june-3-7-in-san-jose/)
+announcing the company's annual developer conference
+featured an illustration of a robot head (🤖)
+bursting with code symbols, emoji, and Apple iconography.
+
+{% asset "wwdc-2019.jpg" alt="WWDC 2019 Banner" %}
+
+All last week,
+attendees saw this motif in slides and signage
+throughout McEnery Convention Center,
+with variations featuring heads of
+monkeys (🐵), aliens (👽), skulls (💀),
+and other Animoji characters.
+
+{% info %}
+
+Looking back,
+it's obvious now that the `#WWDC19` graphics,
+with their neon sign motif,
+were a cheeky hint at the (then rumored) Dark Mode announced in iOS 13.
+Though we must admit the nod to
+[SF Symbols](https://developer.apple.com/design/human-interface-guidelines/sf-symbols/overview/) and the new
+[`UIImage(systemName:)` APIs](https://developer.apple.com/documentation/uikit/uiimage/configuring_and_displaying_symbol_images_in_your_ui)
+(arguably the _real_ dark horse of the conference)
+completely went over our heads.
+
+{% endinfo %}
+
+Beyond presenting a compelling case for several 🤯-based additions to
+Unicode's [recommended emoji ZWJ sequences](https://www.unicode.org/emoji/charts/emoji-zwj-sequences.html),
+these banners reinforced a central theme of the conference:
+_"Write code. Blow minds."_
+Apple's slogan was accurate but ambiguous;
+it wasn't us that was writing code, but Apple.
+We were the ones getting our `#mindsblown`.
+
+The problem with having your mind blown is that you can't think straight.
+
+Now that we're safely removed from the Reality Distortion Field™
+(some of us recovering from `#dubdubflu` 🤒, perhaps),
+let's take a moment to process the announcements from last week as a whole,
+and consider what they mean for
+next week, next month, next year, and the years to come.
+
+---
+
+## SwiftUI, Swift, and Open Source
+
+SwiftUI is the most exciting announcement from Apple
+since unveiling the Swift language itself five years prior.
+
+That said,
+for a language being developed in the open
+with a rigorous process for vetting proposed changes,
+many of us in the community thought
+Swift was immune from surprise announcements at WWDC.
+For better or for worse, we were wrong.
+
+At the [Platforms State of the Union](https://developer.apple.com/videos/play/wwdc2019/103/),
+few of us in the audience recognized the SwiftUI code.
+It _looked_ like the Swift we know and love,
+but it had a bunch of new adornments
+and seemed to play fast and loose with our understanding of syntax rules.
+
+```swift
+import SwiftUI
+
+struct Content: View {
+ @State var model = <#...#>
+
+ var body: some View { <#...#> }
+}
+```
+
+Even if you ardently followed the [Swift Forums](https://forums.swift.org)
+and knew about the proposals for
+[property wrappers](/propertywrapper/),
+[implicit returns for single-line expressions](https://github.com/apple/swift-evolution/blob/master/proposals/0255-omit-return.md), and
+[opaque result types](https://github.com/apple/swift-evolution/blob/master/proposals/0244-opaque-result-types.md)...
+you still might struggle to
+[make sense of everything you saw](https://forums.swift.org/t/what-is-this-thing-from-wwdc/25160).
+
+### Apple's language
+
+The SwiftUI announcement serves a reminder that Swift is Apple's language.
+
+Fortunately,
+this arrangement has worked out pretty well so far ---
+thanks in large part to good faith efforts from Apple engineers
+to communicate openly and
+take their responsibility as stewards of the language seriously.
+Case in point:
+within hours of the Keynote,
+Swift's project leader, Ted Kremenek,
+[posted a message](https://forums.swift.org/t/important-evolution-discussion-of-the-new-dsl-feature-behind-swiftui/25168)
+outlining the situation,
+and [proposals for the new features](https://forums.swift.org/t/function-builders/25167)
+were submitted for review the same day
+_(though one might
+[wonder what that means in practice](https://forums.swift.org/t/important-evolution-discussion-of-the-new-dsl-feature-behind-swiftui/25168/5))_.
+Also, to their credit,
+the rest of the Core Team has done an outstanding job
+communicating with attendees at labs and answering questions on social media.
+
+What happened at WWDC this year underscores a central tension between
+open community-driven contributions
+and the closed product-driven development within Apple.
+There's already an [active discussion](https://forums.swift.org/t/swiftui-for-non-apple-platforms-like-android-web-windows/25455/34)
+about how this dynamic can and should play out for SwiftUI and Combine.
+
+Ultimately, it's up to Apple to decide whether these technologies
+will be incorporated into the Swift open source project.
+However, I'm optimistic that they'll make the right choice in due course.
+
+## SwiftUI, Catalyst, and the Long-Term Viability of Apple Platforms
+
+I don't think it's an exaggeration to say that
+Apple's announcements of Catalyst and SwiftUI this year
+saved macOS from becoming obsolete as a platform.
+
+With AppKit's increasing irrelevance in the era of UIKit,
+few businesses have been able to justify investment into native macOS apps.
+As a result,
+the macOS ecosystem has languished,
+becoming a shadow of its glory days of the
+[delicious generation](https://arstechnica.com/gadgets/2007/05/the-delicious-generation-strikes-back/).
+
+At WWDC,
+Apple laid out a clear path forward for developers:
+
+- **If you're working on an existing iOS app**,
+ focus on making the most of the new features on iOS 13,
+ like Dark Mode support and Sign In with Apple ID.
+- **If you have an iPad app**,
+ consider adding support for macOS Catalina by way of Catalyst.
+- **If you're creating your n+1th app**,
+ weigh the potential benefit of adopting SwiftUI
+ against the costs and risks associated with developing with new technologies
+ (this calculation will change over time).
+- **If you're looking to break into app development, or want to experiment**,
+ start a new Xcode project, check the box next to SwiftUI, and have a blast!
+ There's never been a better time to try everything out.
+
+That's essentially the decision tree Apple would have us follow.
+
+As others have eloquently [opined](https://inessential.com/2019/06/07/the_next_era_ends_the_swift_era_begins),
+SwiftUI marks an important transition for Apple as a company
+from the NeXT era to the Swift era.
+By way of historical analogy:
+
+- Swift is the new [Objective-C](https://en.wikipedia.org/wiki/Objective-C)
+- SwiftUI is the new [Cocoa](https://en.wikipedia.org/wiki/Cocoa_%28API%29)
+- Catalyst is the new [Carbon](https://en.wikipedia.org/wiki/Carbon_%28API%29)
+
+However,
+something's missing from this narrative
+of how we got from Macintosh OS to Mac OS X.
+
+### What about Java(Script)?
+
+> Everyone knows Java was deprecated in Mac OS X 10.4.
+> What this section presupposes is... maybe it didn't?
+
+The transition from C to Objective-C may seem inevitable today,
+but at the time,
+there was a real possibility that Objective-C
+would be replaced by a more popular language ---
+namely, [Java](https://en.wikipedia.org/wiki/Cocoa_%28API%29#Implementations_and_binding).
+
+The situation in 2019 bears a striking resemblance to what happened in 1999.
+
+With web technologies only becoming more capable and convenient over time,
+can you really blame them for going with a "hybrid solution" ---
+especially one that promises to target every platform with a single code base?
+
+We must remember that the purpose of software is to solve problems.
+Consumers only care about which technologies we decide to use
+insofar as it provides a better solution, more quickly, at a lower price.
+
+Yes, there are plenty of examples of cross-platform or web-based technologies
+that look and feel janky and out of place on the Mac or an iPhone.
+But there are plenty of instances where the results are good
+_(or at least "good enough")_.
+
+Don't let perfect be the enemy of good.
+
+Or, put another way:
+
+> 1GB of RAM for an Electron app isn't great,
+> but it's still better than the 0GB of RAM
+> for a native app that never ships.
+
+{% info %}
+
+With Apple's endorsement of declarative programming with SwiftUI,
+now's a great time to see how other communities are solving similar problems.
+For starters,
+check out
+[React](https://reactjs.org),
+[Elm](https://elm-lang.org),
+[Flutter](https://flutter.dev),
+[Android Jetpack Compose](https://developer.android.com/jetpack/compose/),
+and _(our personal favorite)_ [Shoes.rb](http://shoesrb.com).
+
+{% endinfo %}
+
+### Thinking outside the App Store
+
+On the heels of Catalyst's official debut,
+[Twitter announced plans to port its iPad app to the Mac](https://twitter.com/TwitterSupport/status/1135642792665919488?ref_src=twsrc%5Etfw).
+
+> Apple's exciting new technology empowers Twitter to
+> easily bring our entire 1.5 million line code base from iOS to the Mac,
+> allowing ongoing full feature parity with our iPad app,
+> and enhanced with the macOS experience
+> that will make Twitter feel right at home on your Mac.
+> [@TwitterSupport](https://twitter.com/TwitterSupport/status/1135642794473558017)
+
+In fact,
+you don't need to wait for macOS Catalina ---
+you can experience Twitter as a native desktop app today!
+The latest version of Chrome
+[lets you install progressive web apps (PWAs), like Twitter, as native desktop apps](https://developers.google.com/web/progressive-web-apps/desktop#app-window).
+
+Go ahead and try it for yourself:
+open `https://twitter.com/` in Chrome,
+click the ⠇ icon on the far right side of your address bar
+and select "Install Twitter".
+
+
+
+
+
+
+
+
+Android has a similar feature that
+[generates and installs a native app from a web app manifest](https://developers.google.com/web/fundamentals/integration/webapks) ---
+all without ever leaving the browser.
+Meanwhile,
+the Google Play Store has started to become more browser-like itself;
+with [instant apps and games](https://blog.google/products/google-play/introducing-google-play-instant-faster-way-try-apps-and-games/),
+that can be launched immediately,
+without installing the full payload.
+
+And lest you think this is just a Google thing,
+Apple has also (quietly) improved its own support of PWAs over the years.
+iOS 12.2, in particular, was a huge step forward,
+addressing some of the most pressing shortcomings on the platform.
+And the first iOS 13 beta promises to be even better for PWAs
+(though you'd never know it based on this year's conference sessions).
+
+We're not quite there yet,
+but with forthcoming support for
+[Web App Manifest in WebKit](https://webkit.org/status/#specification-web-app-manifest),
+we're getting _really_ close.
+
+When the iPhone first debuted in 2007,
+the intent was for 3rd-party developers to distribute software through web apps.
+The strong distinction between native and web apps
+is a product of history,
+not an immutable truth about software.
+When you think about the future of "apps",
+you have to wonder if this distinction will still make sense in 5 or 10 years.
+Aside from ubiquitous high-speed wireless internet,
+there's nothing particularly futuristic about
+downloading a 50MB payload from the App Store.
+
+{% info %}
+
+I think Apple has a real chance at checking JavaScript hegemony
+with Swift and SwiftUI.
+
+Imagine a version of SwiftUI that could ---
+in addition to targeting AppKit and UIKit ---
+render HTML to web clients.
+Take the very proposal for
+[function builders](https://forums.swift.org/t/function-builders/25167) itself,
+which is at the heart of SwiftUI's DSL:
+
+```swift
+div {
+ if useChapterTitles {
+ h1("1. Loomings.")
+ }
+ p {
+ "Call me Ishmael. Some years ago"
+ }
+ p {
+ "There is now your insular city"
+ }
+}
+```
+
+On its own,
+as a DSL for writing HTML
+(say, as a replacement for
+[Stencil](https://github.com/stencilproject/Stencil) or
+[Leaf](https://github.com/vapor/leaf))...
+that's not particularly compelling.
+
+But what if there was a web framework that could
+automatically generate and serve [Web Assembly](https://swiftwasm.org)
+to be executed on the client
+and manage data flow transparently via XHR, web sockets, or WebRTC?
+Something like
+[Phoenix channels](https://hexdocs.pm/phoenix/channels.html) or
+[Meteor](https://www.meteor.com), perhaps?
+
+If someone built that,
+I think you'd have a solid case for Swift on the Server.
+
+{% endinfo %}
+
+## Apple, Politics, and the Global Economy
+
+Apple's transition from the NeXT era to the Swift era
+goes beyond technical changes within the company.
+
+The day after delivering the WWDC Keynote,
+Tim Cook sat down for
+[an interview with CBS News](https://www.cbsnews.com/news/tim-cook-interview-apple-ceo-insists-company-is-not-a-monopoly-exclusive-today-2019-06-04/),
+in which he defended Apple against allegations of monopolistic behavior.
+A month prior,
+the U.S. Supreme Court
+issued a decision in
+[Apple Inc. v. Pepper ](https://en.wikipedia.org/wiki/Apple_Inc._v._Pepper)
+affirming that consumers were "direct purchasers" of apps from the App Store,
+and could sue Apple for antitrust practices.
+All of this at a time when there's growing political momentum in the U.S.
+to break up big tech companies like Google, Amazon, Facebook, and Apple.
+
+{% info %}
+
+In case you were wondering what prompted
+developer feedback email you got from Apple
+or that
+["App Store Principles and Practices"](https://www.apple.com/ios/app-store/principles-practices/)
+thing they posted ahead of the conference...
+
+{% endinfo %}
+
+Meanwhile,
+slowing iPhone sales in the U.S. and Europe,
+difficulty entering emerging markets in China and India,
+and the threat of a prolonged U.S. / China trade war
+loom large over Apple's future.
+
+For the past decade,
+the App Store has shielded many of us from the complexities of doing business
+with the promise of being able to build an app and carve out a livelihood.
+To the extent that this was ever true,
+we may soon have to confront some deeply held assumptions about our industry.
+
+---
+
+This is all to say that there's a big world outside the Apple ecosystem.
+
+I don't doubt that SwiftUI is the future of macOS and iOS development.
+I'm thrilled that Apple is embracing a declarative UI paradigm on its platforms,
+and couldn't be more excited for functional reactive ideas
+to spread to millions of new developers as a result.
+
+I just worry that fixating on SwiftUI
+will come at the expense of solving more important problems.
+
+## Mind the Enthusiasm Gap
+
+At the time of writing,
+SwiftUI has been public for what... like a week?
+And yet,
+if you haven't spent every waking hour playing with SwiftUI,
+you might have the impression that you're already too late.
+
+Before you drop everything to catch up on session videos
+or start to feel bad for "falling behind,"
+remember that we've been here before.
+
+The Swift programming language
+took a _long_ time to become viable for app development.
+Years.
+Today, Swift 5 is barely recognizable from what we first saw in 2014.
+
+All of those developers who rushed to learn it when it first came out
+_(myself included)_
+how many of us look back on all of that code churn fondly?
+How many teams would have been better off
+holding onto their Objective-C code bases for a few more years
+instead of deciding to rewrite everything from scratch at the first chance?
+
+By all means,
+try out Xcode 11 on the latest macOS Catalina beta
+to appreciate the full extent of its live canvas.
+_It's insanely great!_
+
+But don't feel beholden to it.
+Don't upend your work for the sake of staying ahead of the curve.
+There are plenty of other things that you can focus on now
+that will have a more immediate impact on your users and your own career.
+
+### Silver Bullets and Universal Truths
+
+SwiftUI solves some of the most visible problems facing Apple platforms,
+but they aren't the most important or the most difficult ones ---
+not by a long shot.
+
+Taking care of yourself ---
+sleeping enough, eating right, exercising regularly ---
+will do more to improve your productivity
+than any language or framework out there.
+Your ability to communicate and collaborate with others
+will always be a better predictor of success
+than your choice of technology stack.
+Your relationships with others
+are the most significant factors of success and happiness in life.
+
+---
+
+Apple's annual release cycle
+affords us all an opportunity each June to reflect on how far we've come
+and where we want to go from here ---
+both individually and as a community.
+
+Don't be afraid to think different.
diff --git a/2019-06-24-propertywrapper.md b/2019-06-24-propertywrapper.md
new file mode 100644
index 00000000..50399ae9
--- /dev/null
+++ b/2019-06-24-propertywrapper.md
@@ -0,0 +1,950 @@
+---
+title: Swift Property Wrappers
+author: Mattt
+category: Swift
+excerpt: >-
+ Swift property wrappers go a long way to making SwiftUI possible,
+ but they may play an even more important role
+ in shaping the future of the language as a whole.
+status:
+ swift: 5.1
+---
+
+Years ago,
+we [remarked](/at-compiler-directives/) that the "at sign" (`@`) ---
+along with square brackets and ridiculously-long method names ---
+was a defining characteristic of Objective-C.
+Then came Swift,
+and with it an end to these curious little 🥨-shaped glyphs
+...or so we thought.
+
+At first,
+the function of `@` was limited to Objective-C interoperability:
+`@IBAction`, `@NSCopying`, `@UIApplicationMain`, and so on.
+But in time,
+Swift has continued to incorporate an ever-increasing number of `@`-prefixed
+[attributes](https://docs.swift.org/swift-book/ReferenceManual/Attributes.html).
+
+We got our first glimpse of Swift 5.1 at [WWDC 2019](/wwdc-2019/)
+by way of the SwiftUI announcement.
+And with each "mind-blowing" slide came a hitherto unknown attribute:
+`@State`, `@Binding`, `@EnvironmentObject`...
+
+We saw the future of Swift,
+and it was full of `@`s.
+
+---
+
+We'll dive into SwiftUI once it's had a bit longer to bake.
+
+But this week,
+we wanted to take a closer look at a key language feature for SwiftUI ---
+something that will have arguably the biggest impact on the
+«je ne sais quoi» of Swift in version 5.1 and beyond:
+property wrappers
+
+---
+
+## About Property Delegates Wrappers
+
+Property wrappers were first
+[pitched to the Swift forums](https://forums.swift.org/t/pitch-property-delegates/21895)
+back in March of 2019 ---
+months before the public announcement of SwiftUI.
+
+In his original pitch,
+Swift Core Team member Douglas Gregor
+described the feature (then called _"property delegates"_)
+as a user-accessible generalization of functionality
+currently provided by language features like the `lazy` keyword.
+
+Laziness is a virtue in programming,
+and this kind of broadly useful functionality
+is characteristic of the thoughtful design decisions
+that make Swift such a nice language to work with.
+When a property is declared as `lazy`,
+it defers initialization of its default value until first access.
+For example,
+you could implement equivalent functionality yourself
+using a private property whose access is wrapped by a computed property,
+but a single `lazy` keyword makes all of that unnecessary.
+
+
+{::nomarkdown}
+Expand to lazily evaluate this code expression.
+{:/}
+
+```swift
+struct <#Structure#> {
+ // Deferred property initialization with lazy keyword
+ lazy var deferred = <#...#>
+
+ // Equivalent behavior without lazy keyword
+ private var _deferred: <#Type#>?
+ var deferred: <#Type#> {
+ get {
+ if let value = _deferred { return value }
+ let initialValue = <#...#>
+ _deferred = initialValue
+ return initialValue
+ }
+
+ set {
+ _deferred = newValue
+ }
+ }
+}
+```
+
+
+
+[SE-0258: Property Wrappers](https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md)
+is currently in its third review
+(scheduled to end yesterday, at the time of publication),
+and it promises to open up functionality like `lazy`
+so that library authors can implement similar functionality themselves.
+
+The proposal does an excellent job outlining its design and implementation.
+So rather than attempt to improve on this explanation,
+we thought it'd be interesting to look at
+some new patterns that property wrappers make possible ---
+and, in the process,
+get a better handle on how we might use this feature in our projects.
+
+So, for your consideration,
+here are four potential use cases for the new `@propertyWrapper` attribute:
+
+- [Constraining Values](#constraining-values)
+- [Transforming Values on Property Assignment](#transforming-values-on-property-assignment)
+- [Changing Synthesized Equality and Comparison Semantics](#changing-synthesized-equality-and-comparison-semantics)
+- [Auditing Property Access](#auditing-property-access)
+
+---
+
+## Constraining Values
+
+SE-0258 offers plenty of practical examples, including
+`@Lazy`, `@Atomic`, `@ThreadSpecific`, and `@Box`.
+But the one we were most excited about
+was that of the `@Constrained` property wrapper.
+
+Swift's standard library offer [correct](https://en.wikipedia.org/wiki/IEEE_754),
+performant, floating-point number types,
+and you can have it in any size you want ---
+so long as it's
+[32](https://developer.apple.com/documentation/swift/float) or
+[64](https://developer.apple.com/documentation/swift/double)
+(or [80](https://developer.apple.com/documentation/swift/float80)) bits long
+_([to paraphrase Henry Ford](https://en.wikiquote.org/wiki/Henry_Ford))_.
+
+If you wanted to implement a custom floating-point number type
+that enforced a valid range of values,
+this has been possible since
+[Swift 3](https://github.com/apple/swift-evolution/blob/master/proposals/0067-floating-point-protocols.md).
+However, doing so would require conformance to a
+labyrinth of protocol requirements.
+
+{::nomarkdown}
+
+
+
+{% asset swift-property-wrappers-number-protocols.svg @inline %}
+
+
+
+Credit:
+Flight School Guide to Swift Numbers
+
+
+
+{:/}
+
+Pulling this off is no small feat,
+and often far too much work to justify
+for most use cases.
+
+Fortunately,
+property wrappers offer a way to parameterize
+standard number types with significantly less effort.
+
+### Implementing a value clamping property wrapper
+
+Consider the following `Clamping` structure.
+As a property wrapper (denoted by the `@propertyWrapper` attribute),
+it automatically "clamps" out-of-bound values
+within the prescribed range.
+
+```swift
+@propertyWrapper
+struct Clamping {
+ var value: Value
+ let range: ClosedRange
+
+ init(initialValue value: Value, _ range: ClosedRange) {
+ precondition(range.contains(value))
+ self.value = value
+ self.range = range
+ }
+
+ var wrappedValue: Value {
+ get { value }
+ set { value = min(max(range.lowerBound, newValue), range.upperBound) }
+ }
+}
+```
+
+You could use `@Clamping`
+to guarantee that a property modeling
+[acidity in a chemical solution](https://en.wikipedia.org/wiki/PH)
+within the conventional range of 0 – 14.
+
+```swift
+struct Solution {
+ @Clamping(0...14) var pH: Double = 7.0
+}
+
+let carbonicAcid = Solution(pH: 4.68) // at 1 mM under standard conditions
+```
+
+Attempting to set pH values outside that range
+results in the closest boundary value (minimum or maximum)
+to be used instead.
+
+```swift
+let superDuperAcid = Solution(pH: -1)
+superDuperAcid.pH // 0
+```
+
+{% info %}
+
+You can use property wrappers in implementations of other property wrappers.
+For example,
+this `UnitInterval` property wrapper delegates to `@Clamping`
+for constraining values between 0 and 1, inclusive.
+
+```swift
+@propertyWrapper
+struct UnitInterval {
+ @Clamping(0...1)
+ var wrappedValue: Value = .zero
+
+ init(initialValue value: Value) {
+ self.wrappedValue = value
+ }
+}
+```
+
+For example,
+you might use the `@UnitInterval` property wrapper
+to define an `RGB` type that expresses
+red, green, blue intensities as percentages.
+
+```swift
+struct RGB {
+ @UnitInterval var red: Double
+ @UnitInterval var green: Double
+ @UnitInterval var blue: Double
+}
+
+let cornflowerBlue = RGB(red: 0.392, green: 0.584, blue: 0.929)
+```
+
+{% endinfo %}
+
+#### Related Ideas
+
+- A `@Positive` / `@NonNegative` property wrapper
+ that provides the unsigned guarantees to signed integer types.
+- A `@NonZero` property wrapper
+ that ensures that a number value is either greater than or less than `0`.
+- `@Validated` or `@Whitelisted` / `@Blacklisted` property wrappers
+ that restrict which values can be assigned.
+
+## Transforming Values on Property Assignment
+
+Accepting text input from users
+is a perennial headache among app developers.
+There are just so many things to keep track of,
+from the innocent banalities of string encoding
+to malicious attempts to inject code through a text field.
+But among the most subtle and frustrating problems
+that developers face when accepting user-generated content
+is dealing with leading and trailing whitespace.
+
+A single leading space can
+invalidate URLs,
+confound date parsers,
+and sow chaos by way of off-by-one errors:
+
+```swift
+import Foundation
+
+URL(string: " https://nshipster.com") // nil (!)
+
+ISO8601DateFormatter().date(from: " 2019-06-24") // nil (!)
+
+let words = " Hello, world!".components(separatedBy: .whitespaces)
+words.count // 3 (!)
+```
+
+When it comes to user input,
+clients most often plead ignorance
+and just send everything _as-is_ to the server.
+`¯\_(ツ)_/¯`.
+
+While I'm not advocating for client apps to take on more of this responsibility,
+the situation presents another compelling use case for Swift property wrappers.
+
+---
+
+Foundation bridges the `trimmingCharacters(in:)` method to Swift strings,
+which provides, among other things,
+a convenient way to lop off whitespace
+from both the front or back of a `String` value.
+Calling this method each time you want to ensure data sanity is, however,
+less convenient.
+And if you've ever had to do this yourself to any appreciable extent,
+you've certainly wondered if there might be a better approach.
+
+In your search for a less ad-hoc approach,
+you may have sought redemption through the `willSet` property callback...
+only to be disappointed that you can't use this
+to change events already in motion.
+
+```swift
+struct Post {
+ var title: String {
+ willSet {
+ title = newValue.trimmingCharacters(in: .whitespacesAndNewlines)
+ /* ⚠️ Attempting to store to property 'title' within its own willSet,
+ which is about to be overwritten by the new value */
+ }
+ }
+}
+```
+
+From there,
+you may have realized the potential of `didSet`
+as an avenue for greatness...
+only to realize later that `didSet` isn't called
+during initial property assignment.
+
+```swift
+struct Post {
+ var title: String {
+ // 😓 Not called during initialization
+ didSet {
+ self.title = title.trimmingCharacters(in: .whitespacesAndNewlines)
+ }
+ }
+}
+```
+
+{% info %}
+Setting a property in its own `didSet` callback
+thankfully doesn't cause the callback to fire again,
+so you don't have to worry about accidental infinite self-recursion.
+{% endinfo %}
+
+Undeterred,
+you may have tried any number of other approaches...
+ultimately finding none to yield an acceptable combination of
+ergonomics and performance characteristics.
+
+If any of this rings true to your personal experience,
+you can rejoice in the knowledge that your search is over:
+property wrappers are the solution you've long been waiting for.
+
+### Implementing a Property Wrapper that Trims Whitespace from String Values
+
+Consider the following `Trimmed` struct
+that trims whitespaces and newlines from incoming string values.
+
+```swift
+import Foundation
+
+@propertyWrapper
+struct Trimmed {
+ private(set) var value: String = ""
+
+ var wrappedValue: String {
+ get { value }
+ set { value = newValue.trimmingCharacters(in: .whitespacesAndNewlines) }
+ }
+
+ init(initialValue: String) {
+ self.wrappedValue = initialValue
+ }
+}
+```
+
+By marking each `String` property in the `Post` structure below
+with the `@Trimmed` annotation,
+any string value assigned to `title` or `body` ---
+whether during initialization or via property access afterward ---
+automatically has its leading or trailing whitespace removed.
+
+```swift
+struct Post {
+ @Trimmed var title: String
+ @Trimmed var body: String
+}
+
+let quine = Post(title: " Swift Property Wrappers ", body: "<#...#>")
+quine.title // "Swift Property Wrappers" (no leading or trailing spaces!)
+
+quine.title = " @propertyWrapper "
+quine.title // "@propertyWrapper" (still no leading or trailing spaces!)
+```
+
+#### Related Ideas
+
+- A `@Transformed` property wrapper that applies
+ [ICU transforms](https://developer.apple.com/documentation/foundation/nsstring/1407787-applyingtransform)
+ to incoming string values.
+- A `@Normalized` property wrapper that allows a `String` property
+ to customize its [normalization form](https://unicode.org/reports/tr15/#Norm_Forms).
+- A `@Quantized` / `@Rounded` / `@Truncated` property
+ that quantizes values to a particular degree (e.g. "round up to nearest ½"),
+ but internally tracks precise intermediate values
+ to prevent cascading rounding errors.
+
+## Changing Synthesized Equality and Comparison Semantics
+
+{% warning %}
+This behavior is contingent on an implementation detail
+of synthesized protocol conformance
+and may change before this feature is finalized
+(though we hope this continues to work as described below).
+{% endwarning %}
+
+In Swift,
+two `String` values are considered equal
+if they are [canonically equivalent](https://unicode.org/reports/tr15/#Canon_Compat_Equivalence).
+By adopting these equality semantics,
+Swift strings behave more or less as you'd expect in most circumstances:
+if two strings comprise the same characters,
+it doesn't matter whether any individual character is composed or precomposed
+--- that is,
+"é" (`U+00E9 LATIN SMALL LETTER E WITH ACUTE`)
+is equal to
+"e" (`U+0065 LATIN SMALL LETTER E`) +
+"◌́" (`U+0301 COMBINING ACUTE ACCENT`).
+
+But what if your particular use case calls for different equality semantics?
+Say you wanted a case insensitive notion of string equality?
+
+There are plenty of ways you might go about implementing this today
+using existing language features:
+
+- You could take the `lowercased()` result anytime you do `==` comparison,
+ but as with any manual process, this approach is error-prone.
+- You could create a custom `CaseInsensitive` type that wraps a `String` value,
+ but you'd have to do a lot of additional work to make it
+ as ergonomic and functional as the standard `String` type.
+- You could define a custom comparator function to wrap that comparison ---
+ heck, you could even define your own
+ [custom operator](/swift-operators/#defining-custom-operators) for it ---
+ but nothing comes close to an unqualified `==` between two operands.
+
+None of these options are especially compelling,
+but thanks to property wrappers in Swift 5.1,
+we'll finally have a solution that gives us what we're looking for.
+
+{% info %}
+
+As with numbers,
+Swift takes a protocol-oriented approach
+that delegates string responsibilities
+across a constellation of narrowly-defined types.
+
+{::nomarkdown}
+
+
+For the curious, here's a diagram showing the relationship between all of the string types in the Swift standard library:
+
+
+
+{% asset swift-property-wrappers-string-protocols.svg @inline %}
+
+
+
+Credit:
+Flight School Guide to Swift Strings
+
+
+
+
+
+{:/}
+
+While you _could_ create your own `String`-equivalent type,
+the [documentation](https://developer.apple.com/documentation/swift/stringprotocol)
+carries a strong directive to the contrary:
+
+> Do not declare new conformances to StringProtocol.
+> Only the `String` and `Substring` types in the standard library
+> are valid conforming types.
+
+{% endinfo %}
+
+### Implementing a case-insensitive property wrapper
+
+The `CaseInsensitive` type below
+implements a property wrapper around a `String` / `SubString` value.
+The type conforms to `Comparable` (and by extension, `Equatable`)
+by way of the bridged `NSString` API
+[`caseInsensitiveCompare(_:)`](https://developer.apple.com/documentation/foundation/nsstring/1414769-caseinsensitivecompare):
+
+```swift
+import Foundation
+
+@propertyWrapper
+struct CaseInsensitive {
+ var wrappedValue: Value
+}
+
+extension CaseInsensitive: Comparable {
+ private func compare(_ other: CaseInsensitive) -> ComparisonResult {
+ wrappedValue.caseInsensitiveCompare(other.wrappedValue)
+ }
+
+ static func == (lhs: CaseInsensitive, rhs: CaseInsensitive) -> Bool {
+ lhs.compare(rhs) == .orderedSame
+ }
+
+ static func < (lhs: CaseInsensitive, rhs: CaseInsensitive) -> Bool {
+ lhs.compare(rhs) == .orderedAscending
+ }
+
+ static func > (lhs: CaseInsensitive, rhs: CaseInsensitive) -> Bool {
+ lhs.compare(rhs) == .orderedDescending
+ }
+}
+```
+
+{% info %}
+Although the greater-than operator (`>`)
+[can be derived automatically](https://nshipster.com/equatable-and-comparable/#comparable),
+we implement it here as a performance optimization
+to avoid unnecessary calls to the underlying `caseInsensitiveCompare` method.
+{% endinfo %}
+
+Construct two string values that differ only by case,
+and they'll return `false` for a standard equality check,
+but `true` when wrapped in a `CaseInsensitive` object.
+
+```swift
+let hello: String = "hello"
+let HELLO: String = "HELLO"
+
+hello == HELLO // false
+CaseInsensitive(wrappedValue: hello) == CaseInsensitive(wrappedValue: HELLO) // true
+```
+
+So far, this approach is indistinguishable from
+the custom "wrapper type" approach described above.
+And this is normally where we'd start the long slog of
+implementing conformance to `ExpressibleByStringLiteral`
+and all of the other protocols
+to make `CaseInsensitive` start to feel enough like `String`
+to feel good about our approach.
+
+Property wrappers allow us to forego all of this busywork entirely:
+
+```swift
+struct Account: Equatable {
+ @CaseInsensitive var name: String
+
+ init(name: String) {
+ $name = CaseInsensitive(wrappedValue: name)
+ }
+}
+
+var johnny = Account(name: "johnny")
+let JOHNNY = Account(name: "JOHNNY")
+let Jane = Account(name: "Jane")
+
+johnny == JOHNNY // true
+johnny == Jane // false
+
+johnny.name == JOHNNY.name // false
+
+johnny.name = "Johnny"
+johnny.name // "Johnny"
+```
+
+Here, `Account` objects are checked for equality
+by a case-insensitive comparison on their `name` property value.
+However, when we go to get or set the `name` property,
+it's a _bona fide_ `String` value.
+
+_That's neat, but what's actually going on here?_
+
+Since Swift 4,
+the compiler automatically synthesizes `Equatable` conformance
+to types that adopt it in their declaration
+and whose stored properties are all themselves `Equatable`.
+Because of how compiler synthesis is implemented (at least currently),
+wrapped properties are evaluated through their wrapper
+rather than their underlying value:
+
+```swift
+// Synthesized by Swift Compiler
+extension Account: Equatable {
+ static func == (lhs: Account, rhs: Account) -> Bool {
+ lhs.$name == rhs.$name
+ }
+}
+```
+
+#### Related Ideas
+
+- Defining `@CompatibilityEquivalence`
+ such that wrapped `String` properties with the values `"①"` and `"1"`
+ are considered equal.
+- A `@Approximate` property wrapper to refine
+ equality semantics for floating-point types
+ (See also [SE-0259](https://github.com/apple/swift-evolution/blob/master/proposals/0259-approximately-equal.md))
+- A `@Ranked` property wrapper that takes a function
+ that defines strict ordering for, say, enumerated values;
+ this could allow, for example,
+ the playing card rank `.ace` to be treated either low or high
+ in different contexts.
+
+## Auditing Property Access
+
+Business requirements may stipulate certain controls
+for who can access which records when
+or prescribe some form of accounting for changes over time.
+
+Once again,
+this isn't a task typically performed by, _say_, an iOS app;
+most business logic is defined on the server,
+and most client developers would like to keep it that way.
+But this is yet another use case too compelling to ignore
+as we start to look at the world through property-wrapped glasses.
+
+### Implementing a Property Value Versioning
+
+The following `Versioned` structure functions as a property wrapper
+that intercepts incoming values and creates a timestamped record
+when each value is set.
+
+```swift
+import Foundation
+
+@propertyWrapper
+struct Versioned {
+ private var value: Value
+ private(set) var timestampedValues: [(Date, Value)] = []
+
+ var wrappedValue: Value {
+ get { value }
+
+ set {
+ defer { timestampedValues.append((Date(), value)) }
+ value = newValue
+ }
+ }
+
+ init(initialValue value: Value) {
+ self.wrappedValue = value
+ }
+}
+```
+
+A hypothetical `ExpenseReport` class could
+wrap its `state` property with the `@Versioned` annotation
+to keep a paper trail for each action during processing.
+
+```swift
+class ExpenseReport {
+ enum State { case submitted, received, approved, denied }
+
+ @Versioned var state: State = .submitted
+}
+```
+
+#### Related Ideas
+
+- An `@Audited` property wrapper
+ that logs each time a property is read or written to.
+- A `@Decaying` property wrapper
+ that divides a set number value each time
+ the value is read.
+
+---
+
+However,
+this particular example highlights a major limitation in
+the current implementation of property wrappers
+that stems from a longstanding deficiency of Swift generally:
+**Properties can't be marked as `throws`.**
+
+Without the ability to participate in error handling,
+property wrappers don't provide a reasonable way to
+enforce and communicate policies.
+For example,
+if we wanted to extend the `@Versioned` property wrapper from before
+to prevent `state` from being set to `.approved` after previously being `.denied`,
+our best option is `fatalError()`,
+which isn't really suitable for real applications:
+
+```swift
+class ExpenseReport {
+ @Versioned var state: State = .submitted {
+ willSet {
+ if newValue == .approved,
+ $state.timestampedValues.map { $0.1 }.contains(.denied)
+ {
+ fatalError("J'Accuse!")
+ }
+ }
+ }
+}
+
+var tripExpenses = ExpenseReport()
+tripExpenses.state = .denied
+tripExpenses.state = .approved // Fatal error: "J'Accuse!"
+```
+
+This is just one of several limitations
+that we've encountered so far with property wrappers.
+In the interest of creating a balanced perspective on this new feature,
+we'll use the remainder of this article to enumerate them.
+
+## Limitations
+
+{% warning %}
+
+Some of the shortcomings described below
+may be more a limitation of my current understanding or imagination
+than that of the proposed language feature itself.
+
+Please [reach out](https://twitter.com/NSHipster/)
+with any corrections or suggestions you might have for reconciling them!
+
+{% endwarning %}
+
+### Properties Can't Participate in Error Handling
+
+Properties, unlike functions,
+can't be marked as `throws`.
+
+As it were,
+this is one of the few remaining distinctions between
+these two varieties of type members.
+Because properties have both a getter and a setter,
+it's not entirely clear what the right design would be
+if we were to add error handling ---
+especially when you consider how to play nice with syntax for other concerns
+like access control, custom getters / setters, and callbacks.
+
+As described in the previous section,
+property wrappers have but two methods of recourse
+to deal with invalid values:
+
+1. Ignoring them (silently)
+2. Crashing with `fatalError()`
+
+Neither of these options is particularly great,
+so we'd be very interested by any proposal that addresses this issue.
+
+### Wrapped Properties Can't Be Aliased
+
+Another limitation of the current proposal is that
+you can't use instances of property wrappers as property wrappers.
+
+Our `UnitInterval` example from before,
+which constrains wrapped values between 0 and 1 (inclusive),
+could be succinctly expressed as:
+
+```swift
+typealias UnitInterval = Clamping(0...1) // ❌
+```
+
+However, this isn't possible.
+Nor can you use instances of property wrappers to wrap properties.
+
+```swift
+let UnitInterval = Clamping(0...1)
+struct Solution { @UnitInterval var pH: Double } // ❌
+```
+
+All this actually means in practice is more code replication than would be ideal.
+But given that this problem arises out of a fundamental distinction
+between types and values in the language,
+we can forgive a little duplication if it means avoiding the wrong abstraction.
+
+### Property Wrappers Are Difficult To Compose
+
+Composition of property wrappers is not a commutative operation;
+the order in which you declare them
+affects how they'll behave.
+
+Consider the interplay between an attribute that
+performs [string inflection](https://nshipster.com/valuetransformer/#thinking-forwards-and-backwards)
+and other string transforms.
+For example,
+a composition of property wrappers
+to automatically normalize the URL "slug" in a blog post
+will yield different results if spaces are replaced with dashes
+before or after whitespace is trimmed.
+
+```swift
+struct Post {
+ <#...#>
+ @Dasherized @Trimmed var slug: String
+}
+```
+
+But getting that to work in the first place is easier said than done!
+Attempting to compose two property wrappers that act on `String` values fails,
+because the outermost wrapper is acting on a value of the innermost wrapper type.
+
+```swift
+@propertyWrapper
+struct Dasherized {
+ private(set) var value: String = ""
+
+ var wrappedValue: String {
+ get { value }
+ set { value = newValue.replacingOccurrences(of: " ", with: "-") }
+ }
+
+ init(initialValue: String) {
+ self.wrappedValue = initialValue
+ }
+}
+
+struct Post {
+ <#...#>
+ @Dasherized @Trimmed var slug: String // ⚠️ An internal error occurred.
+}
+```
+
+There's a way to get this to work,
+but it's not entirely obvious or pleasant.
+Whether this is something that can be fixed in the implementation
+or merely redressed by documentation remains to be seen.
+
+### Property Wrappers Aren't First-Class Dependent Types
+
+A dependent type is a type defined by its value.
+For instance,
+"a pair of integers in which the latter is greater than the former" and
+"an array with a prime number of elements"
+are both dependent types
+because their type definition is contingent on its value.
+
+Swift's lack of support for dependent types in its type system
+means that any such guarantees must be enforced at run time.
+
+The good news is that property wrappers
+get closer than any other language feature proposed thus far
+in filling this gap.
+However,
+they still aren't a complete replacement for true value-dependent types.
+
+You can't use property wrappers to,
+for example,
+define a new type with a constraint on which values are possible.
+
+```swift
+typealias pH = @Clamping(0...14) Double // ❌
+func acidity(of: Chemical) -> pH {}
+```
+
+Nor can you use property wrappers
+to annotate key or value types in collections.
+
+```swift
+enum HTTP {
+ struct Request {
+ var headers: [@CaseInsensitive String: String] // ❌
+ }
+}
+```
+
+These shortcomings are by no means deal-breakers;
+property wrappers are extremely useful
+and fill an important gap in the language.
+
+It'll be interesting to see whether the addition of property wrappers
+will create a renewed interest in bringing dependent types to Swift,
+or if they'll be seen as "good enough",
+obviating the need to formalize the concept further.
+
+### Property Wrappers Are Difficult to Document
+
+**Pop Quiz**:
+Which property wrappers are made available by the SwiftUI framework?
+
+Go ahead and visit
+[the official SwiftUI docs](https://developer.apple.com/documentation/swiftui)
+and try to answer.
+
+😬
+
+In fairness, this failure isn't unique to property wrappers.
+
+If you were tasked with determining
+which protocol was responsible for a particular API in the standard library
+or which operators were supported for a pair of types
+based only on what was documented on `developer.apple.com`,
+you're likely to start considering a mid-career pivot away from computers.
+
+This lack of comprehensibility is made all the more dire
+by Swift's increasing complexity.
+
+### Property Wrappers Further Complicate Swift
+
+Swift is a much, _much_ more complex language than Objective-C.
+That's been true since Swift 1.0 and has only become more so over time.
+
+The profusion of `@`-prefixed features in Swift ---
+whether it's
+[`@dynamicMemberLookup`](https://github.com/apple/swift-evolution/blob/master/proposals/0195-dynamic-member-lookup.md)
+and
+[`@dynamicCallable`](https://github.com/apple/swift-evolution/blob/master/proposals/0216-dynamic-callable.md)
+from Swift 4,
+or
+[`@differentiable` and `@memberwise`](https://forums.swift.org/t/pre-pitch-swift-differentiable-programming-design-overview/25992)
+from [Swift for Tensorflow](https://github.com/tensorflow/swift) ---
+makes it increasingly difficult
+to come away with a reasonable understanding of Swift APIs
+based on documentation alone.
+In this respect,
+the introduction of `@propertyWrapper` will be a force multiplier.
+
+How will we make sense of it all?
+(That's a genuine question, not a rhetorical one.)
+
+---
+
+Alright, let's try to wrap this thing up ---
+
+Swift property wrappers allow library authors access to the kind of
+higher-level behavior previously reserved for language features.
+Their potential for improving safety and reducing complexity of code is immense,
+and we've only begun to scratch the surface of what's possible.
+
+Yet, for all of their promise,
+property wrappers and its cohort of language features debuted alongside SwiftUI
+introduce tremendous upheaval to Swift.
+
+Or, as Nataliya Patsovska
+put it in [a tweet](https://twitter.com/nataliya_bg/status/1140519869361926144):
+
+> iOS API design, short history:
+>
+> - Objective C - describe all semantics in the name, the types don’t mean much
+> - Swift 1 to 5 - name focuses on clarity and basic structs, enums, classes and protocols hold semantics
+> - Swift 5.1 - @wrapped \$path @yolo
+>
+> @nataliya_bg
+
+Perhaps we'll only know looking back
+whether Swift 5.1 marked a tipping point or a turning point
+for our beloved language.
diff --git a/2019-07-01-uistackview.md b/2019-07-01-uistackview.md
new file mode 100644
index 00000000..183ec203
--- /dev/null
+++ b/2019-07-01-uistackview.md
@@ -0,0 +1,461 @@
+---
+title: UIStackView
+author: Reda Lemeden
+category: Cocoa
+excerpt: >-
+ Many of us reserve stack views for only the most mundane layouts in iOS.
+ But learn of their myriad capabilities,
+ and you'll come to scoff at the idea of
+ fiddling with Auto Layout constraints yourself ever again.
+status:
+ swift: 5.0
+---
+
+When I was a student in Japan,
+I worked part-time at a restaurant ---
+
+アルバイト
+
+ as the locals call it ---
+where I was tasked with putting away dishes during downtime.
+Every plate had to be stacked neatly,
+ready to serve as the canvas for the next gastronomic creation.
+Lucky for me,
+the universal laws of physics came in quite handy ---
+all I had to do was pile things up, rather indiscriminately, and move on to the next task.
+
+In contrast,
+iOS developers often have to jump through several conceptual hoops when laying out user interfaces.
+After all,
+placing things in an upside-down 2D coordinate system is not intuitive for anyone but geometry geeks;
+for the rest of us, it's not that cut and dried.
+
+But wait ---
+_what if we could take physical world concepts like gravity and elasticity
+and appropriate them for UI layouts?_
+
+As it turns out,
+there has been no shortage of attempts to do so since the early years of
+GUIs
+and personal computing ---
+Motif's [`XmPanedWindow`](http://www.vaxination.ca/motif/XmPanedWind_3X.html) and Swing's [`BoxLayout`](https://docs.oracle.com/javase/tutorial/uiswing/layout/box.html) are notable early specimens.
+These widgets are often referred to as _stack-based layouts_,
+and three decades later,
+they are alive and well on all major platforms,
+including Android's `LinearLayout` and CSS _flexbox_,
+as well as Apple's own `NSStackView`, `UIStackView`, and ---
+new in SwiftUI ---
+`HStack`, `VStack`, and `ZStack`.
+
+This week on NSHipster,
+we invite you to enjoy a multi-course course
+detailing the most delicious morsels
+of this most versatile of layout APIs: `UIStackView`.
+
+---
+
+## Hors-d'œuvres 🍱 Conceptual Overview
+
+Stacking layout widgets come in a wide variety of flavors.
+Even so, they all share one common ingredient:
+leaning on our intuition of the physical world to keep the configuration layer as thin as possible.
+The result is a _declarative_ API that doesn't concern the developer with the minutiæ of view placement and sizing.
+
+If stacking widgets were stoves,
+they'd have two distinct sets of knobs:
+
+- Knobs that affect the _items it contains_
+- Knobs that affect the _stack container itself_
+
+Together, these knobs describe how the available space is allotted;
+whenever a new item is added,
+the stack container recalculates the size and placement of all its contained items,
+and then lets the rendering pipeline take care of the rest.
+In short,
+the raison d’être of any stack container is to ensure that all its child items get a slice of the two-dimensional,
+rectangular pie.
+
+{% info %}
+
+Despite what their name might imply,
+stack views don't exhibit any conventional stacking behaviors (push, pop, etc.).
+Items can be inserted in and removed from any position.
+To avoid this potential misrepresentation, other platforms often talk about _packing_ or _aligning_, instead of _stacking_.
+
+{% endinfo %}
+
+---
+
+## Appetizer 🥗 UIStackView Essentials
+
+Introduced in iOS 9,
+`UIStackView` is the most recent addition to the UI control assortment in Cocoa Touch.
+On the surface,
+it looks similar to its older AppKit sibling, the `NSStackView`,
+but upon closer inspection,
+the differences between the two become clearer.
+
+{% info %}
+
+Throughout this article,
+we occasionally mention other implementations to highlight some of `UIStackView`'s own features and limitations.
+While the APIs may differ, the underlying concepts are the same.
+
+{% endinfo %}
+
+### Managing Subviews
+
+In iOS, the subviews managed by the stack view are referred to as the _arranged subviews_.
+You can initialize a stack view with an array of arranged subviews,
+or add them one by one after the fact. Let's imagine that you have a set of magical plates, the kind that can change their size at will:
+
+```swift
+let saladPlate = UIView(<#...#>)
+let appetizerPlate = UIView(<#...#>)
+
+let plateStack = UIStackView(arrangedSubviews: [saladPlate, appetizerPlate])
+
+// or
+
+let sidePlate = UIView(<#...#>)
+let breadPlate = UIView(<#...#>)
+
+let anotherPlateStack = UIStackView(<#...#>)
+
+anotherPlateStack.addArrangedSubview(sidePlate)
+anotherPlateStack.addArrangedSubview(breadPlate)
+
+// Use the `arrangedSubviews` property to retrieve the plates
+anotherPlateStack.arrangedSubviews.count // 2
+```
+
+You can also insert subviews at a specific index:
+
+```swift
+let chargerPlate = UIView(<#...#>)
+anotherPlateStack.insertArrangedSubview(chargerPlate, at: 1)
+anotherPlateStack.arrangedSubviews.count // 3
+```
+
+{% warning %}
+Stack views don't have an intrinsic content size, so you must set it either implicitly with Auto Layout constraints or explicitly via its `intrinsicContentSize` property. When nested in a scroll view,
+constraints between the stack view and the view containing the scroll view are necessary for things to work as expected.
+{% endwarning %}
+
+Adding an arranged view using any of the methods above also makes it a subview of the stack view.
+To remove an arranged subview that you no longer want around,
+you need to call `removeFromSuperview()` on it.
+The stack view will automatically remove it from the arranged subview list.
+In contrast,
+calling `removeArrangedSubview(_ view: UIView)` on the stack view will only remove the view passed as a parameter from the arranged subview list,
+without removing it from the subview hierarchy.
+Keep this distinction in mind if you are modifying the stack view content during runtime.
+
+```swift
+plateStack.arrangedSubviews.contains(saladPlate) // true
+plateStack.subviews.contains(saladPlate) // true
+
+plateStack.removeArrangedSubview(saladPlate)
+
+plateStack.arrangedSubviews.contains(saladPlate) // false
+plateStack.subviews.contains(saladPlate) // true
+
+saladPlate.removeFromSuperview()
+
+plateStack.arrangedSubviews.contains(saladPlate) // false
+plateStack.subviews.contains(saladPlate) // false
+```
+
+### Toggling Subview Visibility
+
+One major benefit of using stack views over custom layouts is their built-in support for toggling subview visibility without causing layout ambiguity;
+whenever the `isHidden` property is toggled for one of the arranged subviews,
+the layout is recalculated,
+with the possibility to animate the changes inside an animation block:
+
+```swift
+UIView.animate(withDuration: 0.5, animations: {
+ plateStack.arrangedSubviews[0].isHidden = true
+})
+```
+
+This feature is particularly useful when the stack view is part of a reusable view such as table and collection view cells;
+not having to keep track of which constraints to toggle is a bliss.
+
+Now, let's resume our plating work, shall we?
+With everything in place, let's see what can do with our arranged plates.
+
+### Arranging Subviews Horizontally and Vertically
+
+The first stack view property you will likely interact with is the `axis` property.
+Through it you can specify the orientation of the main axis,
+that is the axis along which the arranged subviews will be stacked.
+Setting it to either `horizontal` or `vertical` will force all subviews to fit into a single row or a single column,
+respectively.
+This means that stack views in iOS do not allow overflowing subviews to wrap into a new row or column,
+unlike other implementations such CSS _flexbox_ and its `flex-wrap` property.
+
+{% info %}
+
+This property is often called `orientation` in other platforms,
+including Apple's own `NSStackView`.
+Notwithstanding,
+both iOS and macOS use `vertical`/`horizontal` for the values,
+instead of the less intuitive `row`/`column` that you may come across elsewhere.
+
+{% endinfo %}
+
+The orientation that is _perpendicular_ to the main axis is often referred to as the cross axis.
+Even though this distinction is not explicit in the official documentation,
+it is one of the main ingredients in any stacking algorithm ---
+without it, any attempt at explaining how stack views work will be half-baked.
+
+{::nomarkdown}
+
+
+{% asset uistackview-axes.svg @inline %}
+Stack view axes in horizontal and vertical orientations.
+
+
+{:/}
+
+The default orientation of the main axis in iOS is _horizontal_;
+not ideal for our dishware, so let's fix that:
+
+```swift
+plateStack.axis = .vertical
+```
+
+Et voilà!
+
+---
+
+## Entrée 🍽 Configuring the Layout
+
+When we layout views,
+we're accustomed to thinking in terms of _origin_ and _size_.
+Working with stack views, however, requires us to instead think in terms of _main axis_ and _cross axis_.
+
+Consider how a horizontally-oriented stack view works.
+To determine the width _and_ the `x` coordinate of the origin for each of its arranged subviews,
+it refers to a set of properties that affect layout across the horizontal axis.
+Likewise, to determine the height and the `y` coordinate,
+it refers to another set of properties that affects the vertical axis.
+
+The `UIStackView` class provides axis-specific properties to define the layout: `distribution` for the main axis, and `alignment` for the cross axis.
+
+{% info %}
+
+This pattern is shared among many modern implementations of stacking layouts.
+For instance,
+CSS _flexbox_ uses `justify-content` for the main axis and `align-items` for the cross axis.
+Though not all implementations follow this axis-based paradigm; Android's `LinearLayout`, for example, uses `gravity` for item positioning and `layout_weight` for item sizing along both axes.
+
+{% endinfo %}
+
+### The Main Axis: Distribution
+
+The position and size of arranged subviews along the main axis is affected in part by the value of the `distribution` property,
+and in part by the sizing properties of the subviews themselves.
+
+In practice, each distribution option will determine how space along the main axis is _distributed_ between the subviews.
+With all distributions,
+save for `fillEqually`, the stack view attempts to find an optimal layout based on the intrinsic sizes of the arranged subviews.
+When it can't fill the available space, it stretches
+the arranged subview with the the lowest _content hugging priority_.
+When it can't fit all the arranged subviews,
+it shrinks the one with the lowest _compression resistance priority_.
+If the arranged subviews share the same value for content hugging and compression resistance,
+the algorithm will determine their priority based on their indices.
+
+{% info %}
+
+Some implementations such as CSS _flexbox_ allow setting the weights for each subview manually,
+using the `flex-basis` property.
+In iOS, setting a custom proportional distribution requires additional constraints between the subviews.
+
+{% endinfo %}
+
+With that out of the way, let's take a look at the possible outcomes,
+starting with the distributions that prioritize preserving the intrinsic content size of each arranged subview:
+
+- `equalSpacing`: The stack view gives every arranged subview its intrinsic size alongside the main axis, then introduces equally-sized paddings if there is extra space.
+- `equalCentering`: Similar to `equalSpacing`, but instead of spacing subviews equally, a variably sized padding is introduced in-between so as the center of each subview alongside the axis is equidistant from the two adjacent subview centers.
+
+{::nomarkdown}
+
+
+{% asset uistackview-intrinsic-size-distribution.svg @inline %}
+Examples of equalSpacing and equalCentering in both horizontal and vertical orientations. The dashed lines and values between parentheses represent the intrinsic sizes of each subview.
+
+
+{:/}
+
+In contrast, the following distributions prioritize filling the stack container, regardless of the intrinsic content size of its subviews:
+
+- `fill` (default): The stack view ensures that the arranged subviews fill _all_ the available space. The rules mentioned above apply.
+- `fillProportionally`: Similar to `fill`, but instead of resizing a single view to fill the remaining space, the stack view proportionally resizes all subviews based on their intrinsic content size.
+- `fillEqually`: The stack view ensures that the arranged views fill all the available space _and_ are all the same size along the main axis.
+
+{::nomarkdown}
+
+
+{% asset uistackview-fill-distribution.svg @inline %}
+Examples of fill distributions in both horizontal and vertical orientations.
+
+
+{:/}
+
+{% info %}
+
+Unlike `NSStackView`, `UIStackView` doesn't support gravity-based distribution.
+This solution works by defining gravity areas along the main axis, and placing arranged items in any of them.
+One obvious upside of this approach is the ability to have multiple alignment rules within the same axis.
+On the downside,
+it introduces unnecessary complexity for most use cases.
+
+Without gravity areas,
+there is effectively no way for a `UIStackview` to stack its arranged subviews towards one end of the main axis ---
+a feature that is fairly common elsewhere,
+as is the case with the `flex-start` and `flex-end` values in _flexbox_.
+
+{% endinfo %}
+
+### The Cross Axis: Alignment
+
+The third most important property of `UIStackView` is `alignment`.
+Its value affects the positioning and sizing of arranged subviews along the cross axis.
+That is, the Y axis for horizontal stacks,
+and X axis for vertical stacks.
+You can set it to one of the following values for both vertical and horizontal stacks:
+
+- `fill` (default): The stack view ensures that the arranged views fill _all_ the available space on the cross axis.
+- `leading`/`trailing`: All subviews are aligned to the leading or trailing edge of the stack view along the cross axis. For horizontal stacks, these correspond to the top edge and bottom edge respectively. For vertical stacks, the language direction will affect the outcome: in left-to-right languages the leading edge will correspond to the left, while the trailing one will correspond to the right. The reverse is true for right-to-left languages.
+- `center`: The arranged subviews are centered along the cross axis.
+
+For horizontal stacks, four additional options are available, two of which are redundant:
+
+- `top`: Behaves exactly like `leading`.
+- `firstBaseline`: Behaves like `top`, but uses the first baseline of the subviews instead of their top anchor.
+- `bottom`: Behaves exactly like `trailing`.
+- `lastBaseline`: Behaves like `bottom`, but uses the last baseline of the subviews instead of their bottom anchor.
+
+{% error %}
+Using `firstBaseline` and `lastBaseline` on vertical stacks produces unexpected results. This is a clear shortcoming of the API and a direct result of introducing orientation-specific values to an otherwise orientation-agnostic property.
+{% enderror %}
+
+Coming back to our plates,
+let's make sure that they fill the available vertical space, all while saving the unused horizontal space for other uses ---
+remember, these can shape-shift!
+
+```swift
+plateStack.distribution = .fill
+plateStack.alignment = .leading
+```
+
+## Palate Cleanser 🍧 Background Color
+
+Another quirk of stack views in iOS is that they don't directly support setting a background color. You have to go through their backing layer to do so.
+
+```swift
+plateStack.layer.backgroundColor = UIColor.white.cgColor
+```
+
+Alright, we've come quite far,
+but have a couple of things to go over before our dégustation is over.
+
+---
+
+## Dessert 🍮 Spacing & Auto Layout
+
+By default,
+a stack view sets the spacing between its arranged subviews to zero.
+The value of the `spacing` property is treated as an _exact value_ for distributions that attempt to fill the available space
+(`fill`, `fillEqually`, `fillProportionally`),
+and as a _minimum value_ otherwise (`equalSpacing`, `equalCentering`).
+With fill distributions, negative spacing values cause the subviews to overlap and the last subview to stretch, filling the freed up space.
+Negative `spacing` values have no effect on equal centering or spacing distributions.
+
+```swift
+plateStack.spacing = 2 // These plates can float too!
+```
+
+The spacing property applies equally between each pair of arranged subviews.
+To set an explicit spacing between two particular subviews,
+use the `setCustomSpacing(:after:)` method instead.
+When a custom spacing is used alongside the `equalSpacing` distribution,
+it will be applied on all views,
+not just the one specified in the method call.
+
+To retrieve the custom space later on, `customSpacing(after:)` gives that to you on a silver platter.
+
+```swift
+plateStack.setCustomSpacing(4, after: saladPlate)
+plateStack.customSpacing(after: saladPlate) // 4
+```
+
+{% info %}
+
+When trying to retrieve a non-existent custom spacing, the method will peculiarly return `Float.greatestFiniteMagnitude` (3.402823e+38) instead.
+
+{% endinfo %}
+
+You can apply insets to your stack view
+by setting its `isLayoutMarginsRelativeArrangement` to `true` and assigning a new value to `layoutMargins`.
+
+```swift
+plateStack.isLayoutMarginsRelativeArrangement = true
+plateStack.layoutMargins = UIEdgeInsets(<#...#>)
+```
+
+Sometimes you need more control over the sizing and placement of an arranged subview.
+In those cases,
+you may add custom constraints on top of the ones generated by the stack view.
+Since the latter come with a priority of 1000,
+make sure all of your custom constraints use a priority of 999 or less to avoid unsatisfiable layouts.
+
+```swift
+let constraint = saladPlate.widthAnchor.constraint(equalToConstant: 200)
+constraint.priority = .init(999)
+constraint.isActive = true
+```
+
+For vertical stack views,
+the API lets you calculate distances from the subviews' baselines,
+in addition to their top and bottom edges.
+This comes in handy when trying to maintain a vertical rhythm in text-heavy UIs.
+
+```swift
+plateStack.isBaselineRelativeArrangement = true // Spacing will be measured from the plates' lips, not their wells.
+```
+
+### L’addition s’il vous plaît!
+
+The automatic layout calculation that stack views do for us come with a performance cost.
+In most cases,
+it is negligible.
+But when stack views are nested more than two layers deep,
+the hit could become noticeable.
+
+To be on the safe side,
+avoid using deeply nested stack views,
+especially in reusable views such as table and collection view cells.
+
+## After Dinner Mint 🍬 SwiftUI Stacks
+
+With the introduction of SwiftUI during last month's WWDC,
+Apple gave us a sneak peek at how we will be laying out views in the months and years to come:
+`HStack`, `VStack`, and `ZStack`.
+In broad strokes,
+these views are specialized stacking views where the main axis is pre-defined for each subtype and the alignment configuration is restricted to the corresponding cross axis.
+This is a welcome change that alleviates the `UIStackView` API shortcomings highlighted towards the end of cross axis section above.
+There are more interesting tidbits to go over, but we will leave that for another banquet.
+
+---
+
+Stack views are a lot more versatile than they get credit for.
+Their API on iOS isn't always the most self-explanatory,
+nor is it the most coherent,
+but once you overcome these hurdles,
+you can bend them to your will to achieve non-trivial feats ---
+nothing short of a Michelin star chef boasting their plating prowess.
diff --git a/2019-07-08-caemitterlayer.md b/2019-07-08-caemitterlayer.md
new file mode 100644
index 00000000..976e3fd1
--- /dev/null
+++ b/2019-07-08-caemitterlayer.md
@@ -0,0 +1,728 @@
+---
+title: CAEmitterLayer
+author: Mattt
+category: Cocoa
+excerpt: >-
+ This week mark's NSHipster's 7th anniversary!
+ And what better way to celebrate the occasion
+ than to implement a fun and flexible confetti view on iOS?
+status:
+ swift: 5.0
+---
+
+Etymologically, confetti comes from the Italian word
+for the sugar-coated almond sweet thrown at celebrations,
+which, in turn, get their name from the Latin conficio:
+con- ("with, together") +
+facio ("do, make");
+in another sense, _"to celebrate"_.
+
+Confetti gets tossed around a lot these days,
+but not nearly as in the 20th century
+with its iconic ticker-tape parades
+down the streets of New York City,
+like the one [welcoming home the Apollo 11 astronauts](https://en.wikipedia.org/wiki/Ticker_tape_parade#/media/File:Apollo_11_ticker_tape_parade_1.jpg)
+50 years ago.
+Alas, the rise of digital technology made obsolete the stock tickers
+whose waste paper tape comprised the substrate of those spectacles.
+And as a result, the tradition has become much less commonplace today.
+
+{% info %}
+
+As it were,
+_The Washington Post_ just reported today
+that Monday's ticker-tape parade
+celebrating the U.S. women’s soccer team on their World Cup victory
+was
+[_"The 207th time New Yorkers have dumped office trash on their heroes"_](https://www.washingtonpost.com/history/2019/07/09/uswnts-parade-th-time-new-yorkers-have-dumped-office-trash-their-heroes/).
+
+{% endinfo %}
+
+This week mark's NSHipster's 7th anniversary!
+And what better way to celebrate the occasion
+than to implement a fun and flexible confetti view on iOS?
+
+---
+
+Let's dive right in with a quick refresher on
+the difference between views and layers:
+
+## Views and Layers
+
+On iOS,
+each view is backed by a layer
+...or perhaps it's more accurate to say that layers are _fronted_ by view.
+
+Because despite their reputation as the workhorse of UIKit,
+`UIView` delegates the vast majority of its functionality to `CALayer`.
+Sure, views handle touch events and autoresizing,
+but beyond that,
+nearly everything else between your code and the pixels on screen
+is the responsibility of layers.
+
+Among the available `CALayer` subclasses
+in the Quartz Core / Core Animation framework
+there are APIs for displaying large amounts of content
+by [scrolling](https://developer.apple.com/documentation/quartzcore/cascrolllayer?language=objc)
+and [tiling](https://developer.apple.com/documentation/quartzcore/catiledlayer?language=objc),
+there are APIs for doing
+[advanced](https://developer.apple.com/documentation/quartzcore/careplicatorlayer?language=objc)
+[transformations](https://developer.apple.com/documentation/quartzcore/catransformlayer?language=objc),
+and there are APIs that let you get at the bare
+[metal](https://developer.apple.com/documentation/quartzcore/cametallayer?language=objc).
+But our focus today is a special class called
+[`CAEmitterLayer`](https://developer.apple.com/documentation/quartzcore/caemitterlayer?language=objc).
+
+## Particle Emitters
+
+Indeed, particle systems are frequently used to generate
+fire, smoke, sparks, fireworks, and explosions.
+But they're also capable of modeling... less destructive phenomena like
+rain, snow, sand, and ---
+most importantly ---
+confetti.
+
+`CAEmitterLayer` configures the position and shape of
+where particles are emitted.
+As to the specific behavior and appearance of those particles,
+that's determined by the `CAEmitterCell` objects seeded to the emitter layer.
+
+{% info %}
+
+That said,
+particle emitter layers have properties for configuring
+birth rate, lifetime, velocity, and spin;
+these act as scalars for values set in the constituent emitter cells.
+
+{% endinfo %}
+
+By analogy:
+
+- `CAEmitterLayer`
+ controls the size, position, and intensity of a confetti cannon,
+- `CAEmitterCell`
+ controls the size, shape, color, and movement
+ of each type of confetti loaded into the hopper.
+
+If you wanted confetti with
+black mustaches, orange birds, and [purple aardvarks](/bug-reporting/),
+then you'd load a `CAEmitterLayer` with three different `CAEmitterCell` objects,
+each specifying its `color`, `contents`, and other behaviors.
+
+## Particle Emitter Cells
+
+The secret to `CAEmitterLayer`'s high performance
+is that it doesn't track each particle individually.
+Unlike views or even layers,
+emitted particles can't be altered once they're created.
+_(Also, they don't interact with one another,
+which makes it easy for them to be rendered in parallel)_
+
+Instead,
+`CAEmitterCell` has an enormous API surface area
+to configure every aspect of particle appearance and behavior
+before they're generated, including
+birth rate, lifetime,
+emission angles, velocity, acceleration,
+scale, magnification filter, color ---
+too many to cover in any depth here.
+
+In general,
+most emitter cell behavior is defined by either a single property
+or a group of related properties
+that specify a base value
+along with a corresponding range and/or speed.
+
+A _range_ property specifies the maximum amount
+that can be randomly added or subtracted from the base value.
+For example,
+the `scale` property determines the size of each particle,
+and the `scaleRange` property specifies the
+upper and lower bounds of possible sizes relative to that base value;
+a `scale` of `1.0` and a `scaleRange` of `0.2`
+generates particles sized between
+0.8× and 1.2× the original `contents` size.
+
+Cell emitter behavior may also have a corresponding _speed_ property,
+which specify the rate of growth or decay over the lifetime of the particle.
+For example,
+with the `scaleSpeed` property,
+positive values cause particles to grow over time
+whereas negative values cause particles to shrink.
+
+---
+
+Loaded up with a solid understanding of the ins and outs of `CAEmitterLayer`,
+now's the time for us to let that knowledge spew forth in a flurry of code!
+
+## Implementing a Confetti View for iOS
+
+First,
+let's define an abstraction for the bits of confetti
+that we'd like to shoot from our confetti cannon.
+An enumeration offers the perfect balance of constraints and flexibility
+for our purposes here.
+
+```swift
+enum Content {
+ enum Shape {
+ case circle
+ case triangle
+ case square
+ case custom(CGPath)
+ }
+
+ case shape(Shape, UIColor?)
+ case image(UIImage, UIColor?)
+ case emoji(Character)
+}
+```
+
+Here's how we would configure our confetti cannon
+to shoot out a colorful variety of shapes and images:
+
+```swift
+let contents: [Content] = [
+ .shape(.circle, .purple),
+ .shape(.triangle, .lightGray),
+ .image(UIImage(named: "swift")!, .orange),
+ .emoji("👨🏻"),
+ .emoji("📱")
+]
+```
+
+{% info %}
+
+For brevity,
+we're skipping over how we build shape paths
+or render emoji characters as images,
+but there's some good stuff about `UIGraphicsImageRenderer` in there, too.
+
+
+
+{::nomarkdown}
+
+Expand for implementation details
+
+{:/}
+
+```swift
+fileprivate extension Content {
+ var color: UIColor? {
+ switch self {
+ case let .image(_, color?),
+ let .shape(_, color?):
+ return color
+ default:
+ return nil
+ }
+ }
+
+ var image: UIImage {
+ switch self {
+ case let .image(image, _):
+ return image
+ case let .shape(shape, color):
+ return shape.image(with: color ?? .white)
+ case let .emoji(character):
+ return "\(character)".image()
+ }
+ }
+}
+
+fileprivate extension Content.Shape {
+ func path(in rect: CGRect) -> CGPath {
+ switch self {
+ case .circle:
+ return CGPath(ellipseIn: rect, transform: nil)
+ case .triangle:
+ let path = CGMutablePath()
+ path.addLines(between: [
+ CGPoint(x: rect.midX, y: 0),
+ CGPoint(x: rect.maxX, y: rect.maxY),
+ CGPoint(x: rect.minX, y: rect.maxY),
+ CGPoint(x: rect.midX, y: 0)
+ ])
+ return path
+ case .square:
+ return CGPath(rect: rect, transform: nil)
+ case .custom(let path):
+ return path
+ }
+ }
+
+ func image(with color: UIColor) -> UIImage {
+ let rect = CGRect(origin: .zero, size: CGSize(width: 12.0, height: 12.0))
+ return UIGraphicsImageRenderer(size: rect.size).image { context in
+ context.cgContext.setFillColor(color.cgColor)
+ context.cgContext.addPath(path(in: rect))
+ context.cgContext.fillPath()
+ }
+ }
+}
+
+fileprivate extension String {
+ func image(with font: UIFont = UIFont.systemFont(ofSize: 16.0)) -> UIImage {
+ let string = NSString(string: "\(self)")
+ let attributes: [NSAttributedString.Key: Any] = [
+ .font: font
+ ]
+ let size = string.size(withAttributes: attributes)
+
+ return UIGraphicsImageRenderer(size: size).image { _ in
+ string.draw(at: .zero, withAttributes: attributes)
+ }
+ }
+}
+```
+
+
+
+{% endinfo %}
+
+### Creating a CAEmitterLayer Subclass
+
+The next step is to implement the emitter layer itself.
+
+The primary responsibility of `CAEmitterLayer`
+is to configure its cells.
+Confetti rains down from above
+with just enough variation in its size, speed, and spin to make it interesting.
+We use the passed array of `Content` values
+to set the `contents` of the cell (a `CGImage`)
+and a fill color (a `CGColor`).
+
+```swift
+private final class Layer: CAEmitterLayer {
+ func configure(with contents: [Content]) {
+ emitterCells = contents.map { content in
+ let cell = CAEmitterCell()
+
+ cell.birthRate = 50.0
+ cell.lifetime = 10.0
+ cell.velocity = CGFloat(cell.birthRate * cell.lifetime)
+ cell.velocityRange = cell.velocity / 2
+ cell.emissionLongitude = .pi
+ cell.emissionRange = .pi / 4
+ cell.spinRange = .pi * 6
+ cell.scaleRange = 0.25
+ cell.scale = 1.0 - cell.scaleRange
+ cell.contents = content.image.cgImage
+ if let color = content.color {
+ cell.color = color.cgColor
+ }
+
+ return cell
+ }
+ }
+
+ <#...#>
+}
+```
+
+We'll call this `configure(with:)` method from our confetti view,
+which will be our next and final step.
+
+{% info %}
+
+Although not _strictly_ necessary,
+subclassing `CAEmitterLayer` lets us override `layoutSublayers()`
+and automatically resize and position the emitter
+along the top edge of the layer's frame whenever it changes
+(which you can't do as easily otherwise).
+
+```swift
+ <#...#>
+
+ // MARK: CALayer
+
+ override func layoutSublayers() {
+ super.layoutSublayers()
+
+ emitterShape = .line
+ emitterSize = CGSize(width: frame.size.width, height: 1.0)
+ emitterPosition = CGPoint(x: frame.size.width / 2.0, y: 0)
+ }
+}
+```
+
+{% endinfo %}
+
+### Implementing ConfettiView
+
+We want our confetti view to emit confetti for a certain amount of time
+and then stop.
+However, accomplishing this is surprisingly difficult,
+as evidenced by the questions floating around on
+[Stack Overflow](https://stackoverflow.com/questions/18913484/ios-7-caemitterlayer-spawning-particles-inappropriately).
+
+The central problem is that
+Core Animation operates on its own timeline,
+which doesn't always comport with our own understanding of time.
+
+For instance,
+if you neglect to initialize the `beginTime` of the emitter layer
+with `CACurrentMediaTime()` right before it's displayed,
+it'll render with the wrong time space.
+
+As far as stopping goes:
+you can tell the layer to stop emitting particles
+by setting its `birthRate` property to `0`.
+But if you start it again up
+by resetting that property to `1`,
+you get a flurry of particles filling the screen
+instead of the nice initial burst on the first launch.
+
+Suffice to say that there are myriad different approaches
+to making this behave as expected.
+But here's the best solution we've found
+for handling starting and stopping,
+as well as having more than one emitter at the same time:
+
+---
+
+Going back to our original explanation for a moment,
+each instance of `UIView`
+(or one of its subclasses)
+is backed by a single, corresponding instance of `CALayer`
+(or one of its subclasses).
+A view may also host one or more additional layers,
+either as siblings or children to the backing layer.
+
+Taking advantage of this fact,
+we can create a new emitter layer each time we fire our confetti cannon.
+We can add that layer as a hosted sublayer for our view,
+and let the view handle animation and disposal of each layer
+in a nice, self-contained way.
+
+```swift
+private let kAnimationLayerKey = "com.nshipster.animationLayer"
+
+final class ConfettiView: UIView {
+ func emit(with contents: [Content],
+ for duration: TimeInterval = 3.0)
+ {
+ <#❶#> let layer = Layer()
+ layer.configure(with: contents)
+ layer.frame = self.bounds
+ layer.needsDisplayOnBoundsChange = true
+ self.layer.addSublayer(layer)
+
+ guard duration.isFinite else { return }
+
+
+ <#❷#> let animation = CAKeyframeAnimation(keyPath: #keyPath(CAEmitterLayer.birthRate))
+ animation.duration = duration
+ animation.timingFunction = CAMediaTimingFunction(name: .easeIn)
+ animation.values = [1, 0, 0]
+ animation.keyTimes = [0, 0.5, 1]
+ animation.fillMode = .forwards
+ animation.isRemovedOnCompletion = false
+
+ layer.beginTime = CACurrentMediaTime()
+ layer.birthRate = 1.0
+
+
+ <#❸#> CATransaction.begin()
+ CATransaction.setCompletionBlock {
+ let transition = CATransition()
+ transition.delegate = self
+ transition.type = .fade
+ transition.duration = 1
+ transition.timingFunction = CAMediaTimingFunction(name: .easeOut)
+ transition.setValue(layer, forKey: kAnimationLayerKey) <#❹#>
+ transition.isRemovedOnCompletion = false
+
+ layer.add(transition, forKey: nil)
+
+ layer.opacity = 0
+ }
+ layer.add(animation, forKey: nil)
+ CATransaction.commit()
+ }
+
+ <#...#>
+```
+
+There's a lot of code to unpack here,
+so let's focus on its distinct parts:
+
+❶
+: First, we create an instance of our custom `CAEmitterLayer` subclass
+(creatively named `Layer` here, because it's a private, nested type).
+It's set up with our `configure(with:)` method from before
+and added as a sublayer.
+The `needsDisplayOnBoundsChange` property defaults to `false`
+for whatever reason;
+setting it to `true` here allows us to better handle device trait changes
+(like rotation or moving to a new window).
+
+❷
+: Next, we create a keyframe animation
+to taper the `birthRate` property down to `0` over the specified duration.
+
+❸
+: Then we add that animation in a transaction
+and use the completion block to set up a fade-out transition.
+(We set the view as the transition's animation delegate,
+as described in the next session)
+
+❹
+: Finally, we use key-value coding
+to create a reference between the emitter layer and the transition
+so that it can be referenced and cleaned up later on.
+
+{% info %}
+
+We always want the confetti view to fill the bounds of its superview,
+and we can ensure this behavior by overriding `willMove(toSuperview:)`.
+Another benefit of this is that it allows `ConfettiView` to have
+a simple, unqualified initializer.
+
+```swift
+final class ConfettiView: UIView {
+ init() {
+ super.init(frame: .zero)
+ }
+
+ <#...#>
+
+ // MARK: UIView
+
+ override func willMove(toSuperview newSuperview: UIView?) {
+ guard let superview = newSuperview else { return }
+ frame = superview.bounds
+ }
+}
+```
+
+{% endinfo %}
+
+### Adopting the CAAnimationDelegate Protocol
+
+To extend our overarching metaphor,
+`CAAnimationDelegate` is
+[that little cartoon janitor from _Rocky and Bullwinkle_]({% asset caemitterlayer-janitor.jpg @path %})
+with a push broom at the end of the ticker-tape parade.
+
+The `animationDidStop(_:)` delegate method is called
+when our `CATransition` finishes.
+We then get the reference to the calling layer
+in order to remove all animations and remove it from its superlayer.
+
+```swift
+// MARK: - CAAnimationDelegate
+
+extension ConfettiView: CAAnimationDelegate {
+ func animationDidStop(_ animation: CAAnimation, finished flag: Bool) {
+ if let layer = animation.value(forKey: kAnimationLayerKey) as? CALayer {
+ layer.removeAllAnimations()
+ layer.removeFromSuperlayer()
+ }
+ }
+}
+```
+
+The end result:
+it's as if NSHipster.com were parading down the streets of New York City
+(or rather, Brooklyn, if you really wanted to lean into the hipster thing)
+
+
+
+---
+
+The full `ConfettiView` source
+[is available on GitHub](https://github.com/NSHipster/ConfettiView),
+and can be easily integrated into your project using
+[CocoaPods](https://cocoapods.org).
+
+But ending on such a practical note
+doesn't make for a particularly snazzy conclusion.
+So instead,
+how about a quick bonus round detailing seven other ways
+that you could implement confetti instead:
+
+---
+
+## ✨Bonus ✨ 7 Alternative Approaches to Confetti
+
+### SpriteKit Particle System
+
+SpriteKit is the cooler, younger cousin to UIKit,
+providing nodes to games rather than views to apps.
+On their surface,
+they couldn't look more different from one another.
+And yet both share a common, deep reliance on layers,
+which makes for familiar lower-level APIs.
+
+The comparison between these two frameworks goes even deeper,
+as you'll find if you open
+File > New, scroll down to "Resource"
+and create a new SpriteKit Particle System file.
+Open it up, and Xcode provides a specialized editor
+reminiscent of Interface Builder.
+
+
+
+
+
+
+Call up your designed `SKEmitterNode` by name
+or reimplement in code
+_(if you're the type to hand-roll all of your `UIView`s)_
+for a bespoke confetti experience.
+
+### SceneKit Particle System
+
+Again with the metaphors,
+SceneKit is to 3D what SpriteKit is to 2D.
+
+In Xcode 11,
+open File > New,
+select SceneKit Scene File under the "Resource" heading,
+and you'll find an entire 3D scene editor ---
+right there in your Xcode window.
+
+
+
+
+
+
+Add in a dynamic physics body and a turbulence effect,
+and you can whip up an astonishingly capable simulation in no time at all
+(though if you're like me,
+you may find yourself spending hours just playing around with everything)
+
+{% error %}
+Xcode 10 includes a "SceneKit Particle System File" template ---
+complete with a preset for generating confetti!
+Unfortunately,
+we weren't able to get the editor to work.
+And with Xcode 11,
+this template and the `.scnp` file type has been removed.
+{% enderror %}
+
+### UIKit Dynamics
+
+At the same time that SpriteKit entered the scene,
+you might imagine that UIKit started to get self-conscious about the
+"business only" vibe it was putting out.
+
+So in iOS 7,
+in a desperate attempt to prove itself cool to its "fellow kids"
+UIKit added `UIDynamicAnimator` as part of a set of APIs known as
+"UIKit Dynamics".
+
+Feel free to
+[read our article on `UIFieldBehavior`](/uifieldbehavior/)
+and make confetti out of your app,
+if you like.
+
+### HEVC Video with Alpha Channel
+
+Good news!
+Later this year,
+AVFoundation adds support for alpha channels in HEVC video!
+So if, say, you already had a snazzy After Effects composition of confetti,
+you could export that with a transparent background
+and composite it directly to your app or on the web.
+
+For more details,
+check out
+[WWDC 2019 Session 506](https://developer.apple.com/videos/play/wwdc2019/506/).
+
+### Animated PNGs
+
+Of course,
+this time next year, we'll still be sending animated GIFs around to each other,
+despite all of their shortcomings.
+
+Animated GIFs are especially awful for transparency.
+Without a proper alpha channel,
+GIFs are limited to a single, transparent matte color,
+which causes unsightly artifacts around edges.
+
+We all know to use PNGs on the web for transparent images,
+but only a fraction of us are even aware that
+[APNG](https://en.wikipedia.org/wiki/APNG) is even a thing.
+
+Well even fewer of us know that iOS 13 adds native support for APNG
+(as well as animated GIFs --- finally!).
+Not only were there no announcements at WWDC this year,
+but the only clue of this new feature's existence is
+an API diff between Xcode 11 Beta 2 and 3.
+Here's the documentation stub for the function,
+[`CGAnimateImageAtURLWithBlock`](https://developer.apple.com/documentation/imageio/3333271-cganimateimageaturlwithblock)
+
+And if you think that's (unfortunately) par for the course these days,
+it gets worse.
+Because for whatever reason,
+the relevant header file (`ImageIO/CGImageAnimation.h`)
+is inaccessible in Swift!
+
+Now, we don't really know how this is supposed to work,
+but here's our best guess:
+
+```swift
+// ⚠️ Expected Usage
+let imageView = UIImageView()
+
+let imageURL = URL(fileURLWithPath: "path/to/animated.png")
+let options: [String: Any] = [kCGImageAnimationLoopCount: 42]
+CGAnimateImageAtURLWithBlock(imageURL, options) { (index, cgimage, stop) in
+ imageView.image = UIImage(cgImage: cg)
+}
+```
+
+---
+
+Meanwhile, animated PNGs have been supported in Safari for ages,
+and with a far simpler API:
+
+```html
+
+```
+
+{% asset caemitterlayer-confetti.png loading="lazy" %}
+
+### WebGL
+
+Speaking of the web,
+let's talk about a shiny, new(-ish) web standard called
+[WebGL](https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API).
+
+With just [a few hundred lines of JavaScript and GL shader language]({% asset 'articles/caemitterlayer.js' @path %}),
+you too can render confetti to your very own blog about
+web development
+Objective-C, Swift, and Cocoa.
+
+
+
+{% asset 'logo.png' %}
+
+
+
+{% asset 'articles/caemitterlayer.js' defer %}
+{% asset 'articles/caemitterlayer.css' %}
+
+### Emoji
+
+But really,
+we could do away with all of this confetti
+and express ourselves much more simply:
+
+```swift
+let mood = "🥳"
+```
+
+---
+
+It's hard to believe that it's been seven years since I started this site.
+We've been through a lot together, dear reader,
+so know that your ongoing support means the world to me.
+Thanks for learning with me over the years.
+
+Until next week:
+_May your code continue to compile and inspire._
diff --git a/2019-07-15-formatter.md b/2019-07-15-formatter.md
new file mode 100644
index 00000000..3652e948
--- /dev/null
+++ b/2019-07-15-formatter.md
@@ -0,0 +1,1124 @@
+---
+title: Formatter
+author: Mattt
+category: Cocoa
+tags: nshipster, popular
+excerpt: >-
+ Conversion is a tireless errand in software development.
+ Most programs boil down to some variation of
+ transforming data into something more useful.
+revisions:
+ "2014-06-30": Converted examples to Swift; added iOS 8 & OS X Yosemite formatter classes.
+ "2015-02-17": Converted remaining examples to Swift; reintroduced Objective-C examples; added Objective-C examples for new formatter classes.
+ "2019-07-15": Updated for iOS 13 & macOS 10.15
+status:
+ swift: 5.1
+ reviewed: July 15, 2019
+---
+
+Conversion is a tireless errand in software development.
+Most programs boil down to some variation of
+transforming data into something more useful.
+
+In the case of user-facing software,
+making data human-readable is an essential task ---
+and a complex one at that.
+A user's preferred language, calendar, and currency
+can all factor into how information should be displayed,
+as can other constraints, such as a label's dimensions.
+
+All of this is to say:
+calling `description` on an object just doesn't cut it
+under most circumstances.
+Indeed,
+the real tool for this job is `Formatter`:
+an ancient, abstract class deep in the heart of the Foundation framework
+that's responsible for transforming data into textual representations.
+
+---
+
+`Formatter`'s origins trace back to `NSCell`,
+which is used to display information and accept user input in
+tables, form fields, and other views in AppKit.
+Much of the API design of `(NS)Formatter` reflects this.
+
+Back then,
+formatters came in two flavors: dates and numbers.
+But these days,
+there are formatters for everything from
+physical quantities and time intervals to personal names and postal addresses.
+And as if that weren't enough to keep straight,
+a good portion of these have been
+soft-deprecated,
+or otherwise superseded by more capable APIs (that are also formatters).
+
+{% error %}
+There are so many formatters in Apple SDKs
+that it's impossible to keep them all in working memory.
+Apparently, this is as true of computers as it is for humans;
+at the time of writing,
+[searching for "formatter" on developer.apple.com](https://developer.apple.com/search/?q=formatter&type=Reference)
+fails with a timeout!
+{% enderror %}
+
+To make sense of everything,
+this week's article groups each of the built-in formatters
+into one of four categories:
+
+[Numbers and Quantities](#formatting-numbers-and-quantities)
+: [`NumberFormatter`](#numberformatter)
+: [`MeasurementFormatter`](#measurementformatter)
+
+[Dates, Times, and Durations](#formatting-dates-times-and-durations)
+: [`DateFormatter`](#dateformatter)
+: [`ISO8601DateFormatter`](#iso8601dateformatter)
+: [`DateComponentsFormatter`](#datecomponentsformatter)
+: [`DateIntervalFormatter`](#dateintervalformatter)
+: [`RelativeDateTimeFormatter`](#relativedatetimeformatter)
+
+[People and Places](#formatting-people-and-places)
+: [`PersonNameComponentsFormatter`](#personnamecomponentsformatter)
+: [`CNPostalAddressFormatter`](#cnpostaladdressformatter)
+
+[Lists and Items](#formatting-lists-and-items)
+: [`ListFormatter`](#listformatter)
+
+---
+
+## Formatting Numbers and Quantities
+
+| Class | Example Output | Availability |
+| ---------------------- | --------------- | ---------------------------- |
+| `NumberFormatter` | "1,234.56" | iOS 2.0 macOS 10.0+ |
+| `MeasurementFormatter` | "-9.80665 m/s²" | iOS 10.0+ macOS 10.12+ |
+| `ByteCountFormatter` | "756 KB" | iOS 6.0+ macOS 10.8+ |
+| `EnergyFormatter` | "80 kcal" | iOS 8.0+ macOS 10.10+ |
+| `MassFormatter` | "175 lb" | iOS 8.0+ macOS 10.10+ |
+| `LengthFormatter` | "5 ft, 11 in" | iOS 8.0+ macOS 10.10+ |
+| `MKDistanceFormatter` | "500 miles" | iOS 7.0+ macOS 10.9+ |
+
+{% warning %}
+
+`ByteCountFormatter`,
+`EnergyFormatter`,
+`MassFormatter`,
+`LengthFormatter`, and
+`MKDistanceFormatter`
+are superseded by `MeasurementFormatter`.
+
+| Legacy Formatter | Measurement Formatter Unit |
+| --------------------- | -------------------------- |
+| `ByteCountFormatter` | `UnitInformationStorage` |
+| `EnergyFormatter` | `UnitEnergy` |
+| `MassFormatter` | `UnitMass` |
+| `LengthFormatter` | `UnitLength` |
+| `MKDistanceFormatter` | `UnitLength` |
+
+The only occasions in which you might still use
+`EnergyFormatter`, `MassFormatter`, or `LengthFormatter`
+are when working with the HealthKit framework;
+these formatters provide conversion and interoperability
+with `HKUnit` quantities.
+
+{% endwarning %}
+
+### NumberFormatter
+
+`NumberFormatter` covers every aspect of number formatting imaginable.
+For better or for worse
+_(mostly for better)_,
+this all-in-one API handles
+ordinals and cardinals,
+mathematical and scientific notation,
+percentages,
+and monetary amounts in various flavors.
+It can even write out numbers in a few different languages!
+
+So whenever you reach for `NumberFormatter`,
+the first order of business is to establish
+what _kind_ of number you're working with
+and set the `numberStyle` property accordingly.
+
+#### Number Styles
+
+| Number Style | Example Output |
+| -------------------- | ------------------------ |
+| `none` | 123 |
+| `decimal` | 123.456 |
+| `percent` | 12% |
+| `scientific` | 1.23456789E4 |
+| `spellOut` | one hundred twenty-three |
+| `ordinal` | 3rd |
+| `currency` | \$1234.57 |
+| `currencyAccounting` | (\$1234.57) |
+| `currencyISOCode` | USD1,234.57 |
+| `currencyPlural` | 1,234.57 US dollars |
+
+{% warning %}
+
+`NumberFormatter` also has a `format` property
+that takes a familiar `SPRINTF(3)`-style format string.
+[As we've argued in a previous article](/expressiblebystringinterpolation/),
+format strings are something to be avoided unless absolutely necessary.
+
+{% endwarning %}
+
+#### Rounding & Significant Digits
+
+To prevent numbers from getting annoyingly pedantic
+_("thirty-two point three three --- repeating, of course…")_,
+you'll want to get a handle on `NumberFormatter`'s rounding behavior.
+Here, you have two options:
+
+- Set `usesSignificantDigits` to `true`
+ to format according to the rules of
+ [significant figures](https://en.wikipedia.org/wiki/Significant_figures)
+
+```swift
+var formatter = NumberFormatter()
+formatter.usesSignificantDigits = true
+formatter.minimumSignificantDigits = 1 // default
+formatter.maximumSignificantDigits = 6 // default
+
+formatter.string(from: 1234567) // 1234570
+formatter.string(from: 1234.567) // 1234.57
+formatter.string(from: 100.234567) // 100.235
+formatter.string(from: 1.23000) // 1.23
+formatter.string(from: 0.0000123) // 0.0000123
+```
+
+- Set `usesSignificantDigits` to `false`
+ _(or keep as-is, since that's the default)_
+ to format according to specific limits on
+ how many decimal and fraction digits to show
+ (the number of digits leading or trailing the decimal point, respectively).
+
+```swift
+var formatter = NumberFormatter()
+formatter.usesSignificantDigits = false
+formatter.minimumIntegerDigits = 0 // default
+formatter.maximumIntegerDigits = 42 // default (seriously)
+formatter.minimumFractionDigits = 0 // default
+formatter.maximumFractionDigits = 0 // default
+
+formatter.string(from: 1234567) // 1234567
+formatter.string(from: 1234.567) // 1235
+formatter.string(from: 100.234567) // 100
+formatter.string(from: 1.23000) // 1
+formatter.string(from: 0.0000123) // 0
+```
+
+If you need specific rounding behavior,
+such as "round to the nearest integer" or "round towards zero",
+check out the
+`roundingMode`,
+`roundingIncrement`, and
+`roundingBehavior` properties.
+
+#### Locale Awareness
+
+Nearly everything about the formatter can be customized,
+including the
+grouping separator,
+decimal separator,
+negative symbol,
+percent symbol,
+infinity symbol,
+and
+how to represent zero values.
+
+Although these settings can be overridden on an individual basis,
+it's typically best to defer to the defaults provided by the user's locale.
+
+{% error %}
+
+The advice to defer to user locale defaults has a critical exception:
+**money**
+
+Consider the following code
+that uses the default `NumberFormatter` settings for
+American and Japanese locales
+to format the same number:
+
+```swift
+let number = 1234.5678 // 🤔
+
+let formatter = NumberFormatter()
+formatter.numberStyle = .currency
+
+let 🇺🇸 = Locale(identifier: "en_US")
+formatter.locale = 🇺🇸
+formatter.string(for: number) // $1,234.57
+
+let 🇯🇵 = Locale(identifier: "ja_JP")
+formatter.locale = 🇯🇵
+formatter.string(for: number) // ¥ 1,235 😵
+```
+
+```objc
+NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
+[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
+
+for (NSString *identifier in @[@"en_US", @"ja_JP"]) {
+ numberFormatter.locale = [NSLocale localeWithLocaleIdentifier:identifier];
+ NSLog(@"%@: %@", identifier, [numberFormatter stringFromNumber:@(1234.5678)]);
+}
+// Prints "$1,234.57" and "¥ 1,235" 😵
+```
+
+At the time of writing,
+the difference between \$1,234.57 and ¥ 1,235
+is roughly equivalent to the price difference between
+a new [MacBook Air](https://www.apple.com/shop/buy-mac/macbook-air)
+and
+a [Lightning - 3.5 mm Adapter](https://www.apple.com/jp/shop/product/MMX62J/A/lightning-35-mmヘッドフォンジャックアダプタ).
+Make a mistake like that in your app,
+and someone --- developer or user ---
+is going to be pretty upset.
+
+Working with money in code is a deep topic,
+but the basic guidance is this:
+Use `Decimal` values (rather than `Float` or `Double`)
+and specify an explicit currency.
+For more information,
+check out the
+[Flight School Guide to Swift Numbers](https://flight.school/books/numbers/)
+and its companion Swift library,
+[`Money`](https://github.com/flight-school/money).
+
+{% enderror %}
+
+### MeasurementFormatter
+
+`MeasurementFormatter` was introduced in iOS 10 and macOS 10.12
+as part of the full complement of APIs for performing
+type-safe dimensional calculations:
+
+- `Unit` subclasses represent units of measure,
+ such as count and ratio
+- `Dimension` subclasses represent dimensional units of measure,
+ such as mass and length,
+ (which is the case for the overwhelming majority of
+ the concrete subclasses provided,
+ on account of them being dimensional in nature)
+- A `Measurement` is a quantity of a particular `Unit`
+- A `UnitConverter` converts quantities of one unit to
+ a different, compatible unit
+
+
+
+{::nomarkdown}
+
+For the curious, here's the complete list of units supported by MeasurementFormatter:
+
+{:/}
+
+
+
+| Measure | Unit Subclass | Base Unit |
+| ----------------------------- | --------------------------------- | ----------------------------------- |
+| Acceleration | `UnitAcceleration` | meters per second squared (m/s²) |
+| Planar angle and rotation | `UnitAngle` | degrees (°) |
+| Area | `UnitArea` | square meters (m²) |
+| Concentration of mass | `UnitConcentrationMass` | milligrams per deciliter (mg/dL) |
+| Dispersion | `UnitDispersion` | parts per million (ppm) |
+| Duration | `UnitDuration` | seconds (sec) |
+| Electric charge | `UnitElectricCharge` | coulombs (C) |
+| Electric current | `UnitElectricCurrent` | amperes (A) |
+| Electric potential difference | `UnitElectricPotentialDifference` | volts (V) |
+| Electric resistance | `UnitElectricResistance` | ohms (Ω) |
+| Energy | `UnitEnergy` | joules (J) |
+| Frequency | `UnitFrequency` | hertz (Hz) |
+| Fuel consumption | `UnitFuelEfficiency` | liters per 100 kilometers (L/100km) |
+| Illuminance | `UnitIlluminance` | lux (lx) |
+| Information Storage | `UnitInformationStorage` | Byte\* (byte) |
+| Length | `UnitLength` | meters (m) |
+| Mass | `UnitMass` | kilograms (kg) |
+| Power | `UnitPower` | watts (W) |
+| Pressure | `UnitPressure` | newtons per square meter (N/m²) |
+| Speed | `UnitSpeed` | meters per second (m/s) |
+| Temperature | `UnitTemperature` | kelvin (K) |
+| Volume | `UnitVolume` | liters (L) |
+
+
+
+
+
+
+
+
+
+---
+
+`MeasurementFormatter` and its associated APIs are a intuitive ---
+just a delight to work with, honestly.
+The only potential snag for newcomers to Swift
+(or Objective-C old-timers, perhaps)
+are the use of generics to constrain `Measurement` values
+to a particular `Unit` type.
+
+```swift
+import Foundation
+
+// "The swift (Apus apus) can power itself to a speed of 111.6km/h"
+let speed = Measurement(value: 111.6,
+ unit: .kilometersPerHour)
+
+let formatter = MeasurementFormatter()
+formatter.string(from: speed) // 69.345 mph
+```
+
+#### Configuring the Underlying Number Formatter
+
+By delegating much of its formatting responsibility to
+an underlying `NumberFormatter` property,
+`MeasurementFormatter` maintains a high degree of configurability
+while keeping a small API footprint.
+
+Readers with an engineering background may have noticed that
+the localized speed in the previous example
+gained an extra significant figure along the way.
+As discussed previously,
+we can enable `usesSignificantDigits` and set `maximumSignificantDigits`
+to prevent incidental changes in precision.
+
+```swift
+formatter.numberFormatter.usesSignificantDigits = true
+formatter.numberFormatter.maximumSignificantDigits = 4
+formatter.string(from: speed) // 69.35 mph
+```
+
+#### Changing Which Unit is Displayed
+
+A `MeasurementFormatter`,
+by default,
+will use the preferred unit for the user's current locale (if one exists)
+instead of the one provided by a `Measurement` value.
+
+Readers with a non-American background certainly noticed that
+the localized speed in the original example
+converted to a bizarre, archaic unit of measure known as "miles per hour".
+You can override this default unit localization behavior
+by passing the `providedUnit` option.
+
+```swift
+formatter.unitOptions = [.providedUnit]
+formatter.string(from: speed) // 111.6 km/h
+formatter.string(from: speed.converted(to: .milesPerHour)) // 69.35 mph
+```
+
+---
+
+## Formatting Dates, Times, and Durations
+
+| Class | Example Output | Availability |
+| --------------------------- | ----------------- | ---------------------------- |
+| `DateFormatter` | "July 15, 2019" | iOS 2.0 macOS 10.0+ |
+| `ISO8601DateFormatter` | "2019-07-15" | iOS 10.0+ macOS 10.12+ |
+| `DateComponentsFormatter` | "10 minutes" | iOS 8.0 macOS 10.10+ |
+| `DateIntervalFormatter` | "6/3/19 - 6/7/19" | iOS 8.0 macOS 10.10+ |
+| `RelativeDateTimeFormatter` | "3 weeks ago" | iOS 13.0+ macOS 10.15 |
+
+### DateFormatter
+
+`DateFormatter` is the OG class
+for representing dates and times.
+And it remains your best, first choice
+for the majority of date formatting tasks.
+
+For a while,
+there was a concern that it would become overburdened with responsibilities
+like its sibling `NumberFormatter`.
+But fortunately,
+recent SDK releases spawned new formatters for new functionality.
+We'll talk about those in a little bit.
+
+#### Date and Time Styles
+
+The most important properties for a `DateFormatter` object are its
+`dateStyle` and `timeStyle`.
+As with `NumberFormatter` and its `numberStyle`,
+these date and time styles provide preset configurations
+for common formats.
+
+
+
+
+
Style
+
Date
+
Time
+
+
+
+
+
none
+
“”
+
“”
+
+
+
short
+
“11/16/37”
+
“3:30 PM”
+
+
+
medium
+
“Nov 16, 1937”
+
“3:30:32 PM”
+
+
+
long
+
“November 16, 1937”
+
“3:30:32 PM”
+
+
+
full
+
“Tuesday, November 16, 1937 AD
+
“3:30:42 PM EST”
+
+
+
+
+```swift
+let date = Date()
+
+let formatter = DateFormatter()
+formatter.dateStyle = .long
+formatter.timeStyle = .long
+
+formatter.string(from: date)
+// July 15, 2019 at 9:41:00 AM PST
+
+formatter.dateStyle = .short
+formatter.timeStyle = .short
+formatter.string(from: date)
+// "7/16/19, 9:41:00 AM"
+```
+
+```objc
+NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
+[formatter setDateStyle:NSDateFormatterLongStyle];
+[formatter setTimeStyle:NSDateFormatterLongStyle];
+
+NSLog(@"%@", [formatter stringFromDate:[NSDate date]]);
+// July 15, 2019 at 9:41:00 AM PST
+
+[formatter setDateStyle:NSDateFormatterShortStyle];
+[formatter setTimeStyle:NSDateFormatterShortStyle];
+
+NSLog(@"%@", [formatter stringFromDate:[NSDate date]]);
+// 7/16/19, 9:41:00 AM
+```
+
+`dateStyle` and `timeStyle` are set independently.
+So,
+to display just the time for a particular date,
+for example,
+you set `dateStyle` to `none`:
+
+```swift
+let formatter = DateFormatter()
+formatter.dateStyle = .none
+formatter.timeStyle = .medium
+
+let string = formatter.string(from: Date())
+// 9:41:00 AM
+```
+
+```objc
+NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
+[formatter setDateStyle:NSDateFormatterNoStyle];
+[formatter setTimeStyle:NSDateFormatterMediumStyle];
+
+NSLog(@"%@", [formatter stringFromDate:[NSDate date]]);
+// 9:41:00 AM
+```
+
+As you might expect, each aspect of the date format can alternatively be configured individually, a la carte. For any aspiring time wizards `NSDateFormatter` has a bevy of different knobs and switches to play with.
+
+{% warning %}
+
+`DateFormatter` also has a `dateFormat` property
+that takes a familiar `STRFTIME(3)`-style format string.
+We've already called this out for `NumberFormatter`,
+but it's a point that bears repeating:
+use presets wherever possible and
+only use custom format strings if absolutely necessary.
+
+{% endwarning %}
+
+### ISO8601DateFormatter
+
+When we wrote our first article about `NSFormatter` back in 2013,
+we made a point to include discussion of
+[Peter Hosey's ISO8601DateFormatter](https://github.com/boredzo/iso-8601-date-formatter)'s
+as the essential open-source library
+for parsing timestamps from external data sources.
+
+Fortunately,
+we no longer need to proffer a third-party solution,
+because, as of iOS 10.0 and macOS 10.12,
+`ISO8601DateFormatter` is now built-in to Foundation.
+
+```swift
+let formatter = ISO8601DateFormatter()
+formatter.date(from: "2019-07-15T09:41:00-07:00")
+// Jul 15, 2019 at 9:41 AM
+```
+
+{% info %}
+
+`JSONDecoder` provides built-in support for decoding ISO8601-formatted timestamps
+by way of the `.iso8601` date decoding strategy.
+
+```swift
+import Foundation
+
+let json = #"""
+[{
+ "body": "Hello, world!",
+ "timestamp": "2019-07-15T09:41:00-07:00"
+}]
+"""#.data(using: .utf8)!
+
+struct Comment: Decodable {
+ let body: String
+ let timestamp: Date
+}
+
+let decoder = JSONDecoder()
+decoder.dateDecodingStrategy = .iso8601
+
+let comments = try decoder.decode([Comment].self, from: json)
+comments.first?.timestamp
+// Jul 15, 2019 at 9:41 AM
+```
+
+{% endinfo %}
+
+### DateIntervalFormatter
+
+`DateIntervalFormatter` is like `DateFormatter`,
+but can handle two dates at once ---
+specifically, a start and end date.
+
+```swift
+let formatter = DateIntervalFormatter()
+formatter.dateStyle = .short
+formatter.timeStyle = .none
+
+let fromDate = Date()
+let toDate = Calendar.current.date(byAdding: .day, value: 7, to: fromDate)!
+
+formatter.string(from: fromDate, to: toDate)
+// "7/15/19 – 7/22/19"
+```
+
+```objc
+NSDateIntervalFormatter *formatter = [[NSDateIntervalFormatter alloc] init];
+formatter.dateStyle = NSDateIntervalFormatterShortStyle;
+formatter.timeStyle = NSDateIntervalFormatterNoStyle;
+
+NSDate *fromDate = [NSDate date];
+NSDate *toDate = [fromDate dateByAddingTimeInterval:86400 * 7];
+
+NSLog(@"%@", [formatter stringFromDate:fromDate toDate:toDate]);
+// "7/15/19 – 7/22/19"
+```
+
+#### Date Interval Styles
+
+
+
+
+
Style
+
Date
+
Time
+
+
+
+
+
none
+
“”
+
“”
+
+
+
short
+
“6/30/14 - 7/11/14”
+
“5:51 AM - 7:37 PM”
+
+
+
medium
+
“Jun 30, 2014 - Jul 11, 2014”
+
“5:51:49 AM - 7:38:29 PM”
+
+
+
long
+
“June 30, 2014 - July 11, 2014”
+
“6:02:54 AM GMT-8 - 7:49:34 PM GMT-8”
+
+
+
full
+
“Monday, June 30, 2014 - Friday, July 11, 2014
+
“6:03:28 PM Pacific Standard Time - 7:50:08 PM Pacific Standard Time”
+
+
+
+
+{% info %}
+When displaying business hours,
+such as "Mon – Fri: 8:00 AM – 10:00 PM",
+use the `shortWeekdaySymbols` of the current `Calendar`
+to get localized names for the days of the week.
+
+```swift
+import Foundation
+
+var calendar = Calendar.current
+calendar.locale = Locale(identifier: "en_US")
+calendar.shortWeekdaySymbols
+// ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
+
+calendar.locale = Locale(identifier: "ja_JP")
+calendar.shortWeekdaySymbols
+// ["日", "月", "火", "水", "木", "金", "土"]
+```
+
+{% endinfo %}
+
+### DateComponentsFormatter
+
+As the name implies,
+`DateComponentsFormatter` works with `DateComponents` values
+([previously](/datecomponents/)),
+which contain a combination of discrete calendar quantities,
+such as "1 day and 2 hours".
+
+`DateComponentsFormatter` provides localized representations of date components
+in several different, pre-set formats:
+
+```swift
+let formatter = DateComponentsFormatter()
+formatter.unitsStyle = .full
+
+let components = DateComponents(day: 1, hour: 2)
+
+let string = formatter.string(from: components)
+// 1 day, 2 hours
+```
+
+```objc
+NSDateComponentsFormatter *formatter = [[NSDateComponentsFormatter alloc] init];
+formatter.unitsStyle = NSDateComponentsFormatterUnitsStyleFull;
+
+NSDateComponents *components = [[NSDateComponents alloc] init];
+components.day = 1;
+components.hour = 2;
+
+NSLog(@"%@", [formatter stringFromDateComponents:components]);
+// 1 day, 2 hours
+```
+
+#### Date Components Unit Styles
+
+| Style | Example |
+| ------------- | ----------------------- |
+| `positional` | "1:10" |
+| `abbreviated` | "1h 10m" |
+| `short` | "1hr 10min" |
+| `full` | "1 hour, 10 minutes" |
+| `spellOut` | "One hour, ten minutes" |
+
+#### Formatting Context
+
+Some years ago,
+formatters introduced the concept of formatting context,
+to handle situations where
+the capitalization and punctuation of a localized string may depend on whether
+it appears at the beginning or middle of a sentence.
+A `context` property is available for `DateComponentsFormatter`,
+as well as `DateFormatter`, `NumberFormatter`, and others.
+
+
+
+| Formatting Context | Output |
+| --------------------- | -------------------- |
+| `standalone` | "About 2 hours" |
+| `listItem` | "About 2 hours" |
+| `beginningOfSentence` | "About 2 hours" |
+| `middleOfSentence` | "about 2 hours" |
+| `dynamic` | Depends\* |
+
+
+
\*
+A `Dynamic` context changes capitalization automatically
+depending on where it appears in the text
+for locales that may position strings differently
+depending on the content.
+
+
+
+
+### RelativeDateTimeFormatter
+
+`RelativeDateTimeFormatter` is a newcomer in iOS 13 ---
+and at the time of writing, still undocumented,
+so consider this an NSHipster exclusive scoop!
+
+Longtime readers may recall that
+`DateFormatter` actually gave this a try circa iOS 4
+by way of the `doesRelativeDateFormatting` property.
+But that hardly ever worked,
+and most of us forgot about it, probably.
+Fortunately,
+`RelativeDateTimeFormatter` succeeds
+where `doesRelativeDateFormatting` fell short,
+and offers some great new functionality to make your app
+more personable and accessible.
+
+(As far as we can tell,)
+`RelativeDatetimeFormatter` takes the most significant date component
+and displays it in terms of past or future tense
+("1 day ago" / "in 1 day").
+
+```swift
+let formatter = RelativeDateTimeFormatter()
+
+formatter.localizedString(from: DateComponents(day: 1, hour: 1)) // "in 1 day"
+formatter.localizedString(from: DateComponents(day: -1)) // "1 day ago"
+formatter.localizedString(from: DateComponents(hour: 3)) // "in 3 hours"
+formatter.localizedString(from: DateComponents(minute: 60)) // "in 60 minutes"
+```
+
+For the most part,
+this seems to work really well.
+However, its handling of `nil`, zero, and net-zero values
+leaves something to be desired...
+
+```swift
+formatter.localizedString(from: DateComponents(hour: 0)) // "in 0 hours"
+formatter.localizedString(from: DateComponents(day: 1, hour: -24)) // "in 1 day"
+formatter.localizedString(from: DateComponents()) // ""
+```
+
+#### Styles
+
+
+
+| Style | Example |
+| ------------- | ------------------------- |
+| `abbreviated` | "1 mo. ago" \* |
+| `short` | "1 mo. ago" |
+| `full` | "1 month ago" |
+| `spellOut` | "one month ago" |
+
+
+
\*May produce output distinct from `short` for non-English locales.
+
+
+
+
+#### Using Named Relative Date Times
+
+By default,
+`RelativeDateTimeFormatter` adopts the formulaic convention
+we've seen so far.
+But you can set the `dateTimeStyle` property to `.named`
+to prefer localized deictic expressions ---
+"tomorrow", "yesterday", "next week" ---
+whenever one exists.
+
+```swift
+import Foundation
+
+let formatter = RelativeDateTimeFormatter()
+formatter.localizedString(from: DateComponents(day: -1)) // "1 day ago"
+
+formatter.dateTimeStyle = .named
+formatter.localizedString(from: DateComponents(day: -1)) // "yesterday"
+```
+
+This just goes to show that
+beyond calendrical and temporal relativity,
+`RelativeDateTimeFormatter` is a real whiz at linguistic relativity, too!
+For example,
+English doesn't have a word to describe the day before yesterday,
+whereas other languages, like German, do.
+
+```swift
+formatter.localizedString(from: DateComponents(day: -2)) // "2 days ago"
+
+formatter.locale = Locale(identifier: "de_DE")
+formatter.localizedString(from: DateComponents(day: -2)) // "vorgestern"
+```
+
+Hervorragend!
+
+---
+
+## Formatting People and Places
+
+| Class | Example Output | Availability |
+| ------------------------------- | ------------------------------------------- | --------------------------- |
+| `PersonNameComponentsFormatter` | "J. Appleseed" | iOS 9.0+ macOS 10.11+ |
+| `CNContactFormatter` | "Appleseed, Johnny" | iOS 9.0+ macOS 10.11+ |
+| `CNPostalAddressFormatter` | "1 Infinite Loop\\n Cupertino CA 95014" | iOS 9.0+ macOS 10.11+ |
+
+{% warning %}
+
+`CNContactFormatter`
+is superseded by `PersonNameComponentsFormatter`.
+
+Unless you're working with existing `CNContact` objects,
+prefer the use of `PersonNameComponentsFormatter` to format personal names.
+
+{% endwarning %}
+
+### PersonNameComponentsFormatter
+
+`PersonNameComponentsFormatter` is a sort of high water mark for Foundation.
+It encapsulates one of the [hardest](https://martinfowler.com/bliki/TwoHardThings.html),
+most personal problems in computer
+in such a way to make it accessible to anyone
+without requiring a degree in Ethnography.
+
+The [documentation](https://developer.apple.com/documentation/foundation/personnamecomponentsformatter)
+does a wonderful job illustrating the complexities of personal names
+(if I might say so myself),
+but if you had any doubt of the utility of such an API,
+consider the following example:
+
+```swift
+let formatter = PersonNameComponentsFormatter()
+
+var nameComponents = PersonNameComponents()
+nameComponents.givenName = "Johnny"
+nameComponents.familyName = "Appleseed"
+
+formatter.string(from: nameComponents) // "Johnny Appleseed"
+```
+
+Simple enough, right?
+We all know names are space delimited, first-last... _right?_
+
+```swift
+nameComponents.givenName = "约翰尼"
+nameComponents.familyName = "苹果籽"
+
+formatter.string(from: nameComponents) // "苹果籽约翰尼"
+```
+
+_’nuf said._
+
+### CNPostalAddressFormatter
+
+`CNPostalAddressFormatter` provides a convenient `Formatter`-based API
+to functionality dating back to the original AddressBook framework.
+
+The following example formats a constructed `CNMutablePostalAddress`,
+but you'll most likely use existing `CNPostalAddress` values
+retrieved from the user's address book.
+
+```swift
+let address = CNMutablePostalAddress()
+address.street = "One Apple Park Way"
+address.city = "Cupertino"
+address.state = "CA"
+address.postalCode = "95014"
+
+let addressFormatter = CNPostalAddressFormatter()
+addressFormatter.string(from: address)
+/* "One Apple Park Way
+ Cupertino CA 95014" */
+```
+
+#### Styling Formatted Attributed Strings
+
+When formatting compound values,
+it can be hard to figure out where each component went
+in the final, resulting string.
+This can be a problem when you want to, for example,
+call out certain parts in the UI.
+
+Rather than hacking together an ad-hoc,
+[regex](/swift-regular-expressions/)-based solution,
+`CNPostalAddressFormatter` provides a method that vends an
+`NSAttributedString` that lets you identify
+the ranges of each component
+(`PersonNameComponentsFormatter` does this too).
+
+The `NSAttributedString` API is...
+to put it politely,
+bad.
+It feels bad to use.
+
+So for the sake of anyone hoping to take advantage of this functionality,
+please copy-paste and appropriate the following code sample
+to your heart's content:
+
+```swift
+var attributedString = addressFormatter.attributedString(
+ from: address,
+ withDefaultAttributes: [:]
+ ).mutableCopy() as! NSMutableAttributedString
+
+let stringRange = NSRange(location: 0, length: attributedString.length)
+attributedString.enumerateAttributes(in: stringRange, options: []) { (attributes, attributesRange, _) in
+ let color: UIColor
+ switch attributes[NSAttributedString.Key(CNPostalAddressPropertyAttribute)] as? String {
+ case CNPostalAddressStreetKey:
+ color = .red
+ case CNPostalAddressCityKey:
+ color = .orange
+ case CNPostalAddressStateKey:
+ color = .green
+ case CNPostalAddressPostalCodeKey:
+ color = .purple
+ default:
+ return
+ }
+
+ attributedString.addAttribute(.foregroundColor,
+ value: color,
+ range: attributesRange)
+}
+```
+
+
+
+One Apple Park Way
+Cupertino
+CA
+95014
+
+
+
+---
+
+## Formatting Lists and Items
+
+| Class | Example Output | Availability |
+| --------------- | --------------------------------------- | ---------------------------- |
+| `ListFormatter` | "macOS, iOS, iPadOS, watchOS, and tvOS" | iOS 13.0+ macOS 10.15+ |
+
+### ListFormatter
+
+Rounding out our survey of formatters in the Apple SDK,
+it's another new addition in iOS 13:
+`ListFormatter`.
+To be completely honest,
+we didn't know where to put this in the article,
+so we just kind of stuck it on the end here.
+(Though in hindsight,
+this is perhaps appropriate given the subject matter).
+
+Once again,
+[we don't have any official documentation to work from at the moment](https://developer.apple.com/documentation/foundation/listformatter),
+but the comments in the header file give us enough to go on.
+
+> NSListFormatter provides locale-correct formatting of a list of items
+> using the appropriate separator and conjunction.
+> Note that the list formatter is unaware of
+> the context where the joined string will be used,
+> e.g., in the beginning of the sentence
+> or used as a standalone string in the UI,
+> so it will not provide any sort of capitalization customization on the given items,
+> but merely join them as-is.
+>
+> The string joined this way may not be grammatically correct when placed in a sentence,
+> and it should only be used in a standalone manner.
+
+_tl;dr_:
+This is `joined(by:)` with locale-aware serial and penultimate delimiters.
+
+For simple lists of strings,
+you don't even need to bother with instantiating `ListFormatter` ---
+just call the `localizedString(byJoining:)` class method.
+
+```swift
+import Foundation
+
+let operatingSystems = ["macOS", "iOS", "iPadOS", "watchOS", "tvOS"]
+ListFormatter.localizedString(byJoining: operatingSystems)
+// "macOS, iOS, iPadOS, watchOS, and tvOS"
+```
+
+`ListFormatter` works as you'd expect
+for lists comprising zero, one, or two items.
+
+```swift
+ListFormatter.localizedString(byJoining: [])
+// ""
+
+ListFormatter.localizedString(byJoining: ["Apple"])
+// "Apple"
+
+ListFormatter.localizedString(byJoining: ["Jobs", "Woz"])
+// "Jobs and Woz"
+```
+
+#### Lists of Formatted Values
+
+`ListFormatter` exposes an underlying `itemFormatter` property,
+which effectively adds a `map(_:)` before calling `joined(by:)`.
+You use `itemFormatter` whenever you'd formatting a list of non-String elements.
+For example,
+you can set a `NumberFormatter` as the `itemFormatter` for a `ListFormatter`
+to turn an array of cardinals (`Int` values)
+into a localized list of ordinals.
+
+```swift
+let numberFormatter = NumberFormatter()
+numberFormatter.numberStyle = .ordinal
+
+let listFormatter = ListFormatter()
+listFormatter.itemFormatter = numberFormatter
+
+listFormatter.string(from: [1, 2, 3])
+// "1st, 2nd, and 3rd"
+```
+
+{% warning %}
+
+If you set a custom locale on your list formatter,
+be sure to set that locale for the underlying formatter.
+And be mindful of value semantics, too ---
+without the re-assignment to `itemFormatter` in the example below,
+you'd get a French list of English ordinals instead.
+
+```swift
+let 🇫🇷 = Locale(identifier: "fr_FR")
+listFormatter.locale = 🇫🇷
+
+numberFormatter.locale = 🇫🇷
+listFormatter.itemFormatter = numberFormatter
+
+listFormatter.string(from: [1, 2, 3])
+// "1er, 2e et 3e"
+```
+
+{% endwarning %}
+
+---
+
+As some of the oldest members of the Foundation framework,
+`NSNumberFormatter` and `NSDateFormatter`
+are astonishingly well-suited to their respective domains,
+in that way only decade-old software can.
+This tradition of excellence is carried by the most recent incarnations as well.
+
+If your app deals in numbers or dates
+(or time intervals or names or lists measurements of any kind),
+then `NSFormatter` is indispensable.
+
+And if your app _doesn't_...
+then the question is,
+what _does_ it do, exactly?
+
+Invest in learning all of the secrets of Foundation formatters
+to get everything exactly how you want them.
+And if you find yourself with formatting logic scattered across your app,
+consider creating your own `Formatter` subclass
+to consolidate all of that business logic in one place.
+
+{% asset articles/formatter.css %}
diff --git a/2019-08-12-accessibility-keyboard.md b/2019-08-12-accessibility-keyboard.md
new file mode 100644
index 00000000..2a035ee5
--- /dev/null
+++ b/2019-08-12-accessibility-keyboard.md
@@ -0,0 +1,470 @@
+---
+title: macOS Accessibility Keyboard
+author: Mattt
+category: Miscellaneous
+excerpt: >-
+ Beyond its immediate usefulness as an assistive technology,
+ the Accessibility Keyboard challenges us to think differently
+ about the nature of input methods
+ and any remaining distinction between mobile and desktop computers.
+status:
+ swift: n/a
+---
+
+For a while now,
+the distinction between "desktop" and "mobile"
+has become increasingly tenuous.
+As the computers in our pockets grow ever more capable,
+they more closely resemble the computers typically situated on our desks and laps.
+This trend was especially pronounced in this year's WWDC,
+with the announcement of
+[Catalyst](https://developer.apple.com/ipad-apps-for-mac/) and
+[iPadOS](https://www.apple.com/ipados/).
+
+Today, what's the difference between a MacBook and an iPad?
+Practically speaking, you might point to
+the presence or absence of
+a physical keyboard,
+a SIM card, or
+an ARM processor
+_(and if the rumors about next year's MacBook models are to believed,
+those latter two may soon cease to be a distinction)._
+
+For many of us,
+a physical keyboard is the defining trait that
+_makes_ a computer a "desktop" computer in the traditional sense;
+when you purchase an external keyboard for your iPad,
+you do so to make it "desktop"-like.
+But for many others ---
+including those of us with a physical disability ---
+a typewriter-like keyboard is but one of many input methods
+available to desktop users.
+
+This week on NSHipster,
+we're taking a look at the macOS Accessibility Keyboard.
+Beyond its immediate usefulness as an assistive technology,
+the Accessibility Keyboard challenges us to think differently
+about the nature of input methods
+and any remaining distinction between mobile and desktop computers.
+
+---
+
+Introduced in macOS High Sierra,
+the [Accessibility Keyboard](https://support.apple.com/accessibility/mac)
+lets you type and interact with your Mac
+without the use of a physical keyboard.
+
+To turn it on,
+open System Preferences,
+click the Accessibility preference pane,
+select "Keyboard" under the "Interactions" section in the sidebar.
+(Alternatively, you can search for "Accessibility Keyboard"
+and navigate to the first result).
+
+
+
+
+
+
+Click the checkbox labeled "Enable Accessibility Keyboard"
+to present the accessibility keyboard over the windows of the frontmost app.
+
+{% asset accessibility-keyboard-keyboard.png %}
+
+The software keyboard reproduces the layout of your hardware keyboard.
+The modifier keys outlined in red (⌘, ⇧, ⌥)
+are "sticky keys"
+and remain active until a non-"sticky" key is activated,
+allowing for capital letters and keyboard shortcuts.
+Along the top row are iOS-style suggestions
+that update automatically as you type.
+
+{% info %}
+By default,
+the layout of your current keyboard is used.
+Pictured above is a
+[JIS keyboard](https://en.wikipedia.org/wiki/Japanese_input_method),
+our preferred layout here at NSHipster.
+{% endinfo %}
+
+However, the most interesting feature of the Accessibility Keyboard
+is tucked behind the ⚙︎ button on the top right corner ---
+_the ability to customize and create your own keyboards!_
+
+{% asset accessibility-keyboard-menu-customize.png width=400 %}
+
+## Customizing and Creating Your Own Accessibility Keyboards
+
+Panel Editor is a built-in app
+that lets you edit Accessibility Keyboard panels.
+
+{% asset accessibility-keyboard-panel-editor.png alt="Accessibility Keyboard Panel Editor window and icon" loading="lazy" %}
+
+For something so obscure,
+the Panel Editor app is remarkably well made.
+Adding, moving, and editing buttons on a panel is a cinch.
+You can even click and drag to select and move multiple buttons at once,
+and group buttons together at your convenience.
+
+Each button has a name
+as well as options for font size, color, and position.
+By default, the name appears in the button itself,
+but you can specify an image to display instead.
+
+You can configure a button to perform any one of the following actions
+when clicked:
+
+- None
+- Go Back _(navigate between panels in Accessibility Keyboard)_
+- Open Panel
+- Show / Hide Toolbar
+- Dwell _(relevant to head- or eye-tracking technology, and other hardware switches)_
+- AppleScript
+- Enter Text
+- Press Keys
+- Open App
+- System Event
+- Typing Suggestions
+
+Of these,
+"Enter Text" is the most common.
+We'll use that in our next example
+as a way to solve the problem of
+creating input methods for scripts without a keyboard.
+
+### Creating an IPA Keyboard
+
+Standard Latin script is insufficient for expressing phonetics,
+how a word sounds when spoken.
+As English speakers, we know this all too well.
+That's why linguists invented their own script,
+the International Phonetic Alphabet
+(IPA).
+Whereas typical letters may have different pronunciations
+across dialects (/tə.ˈme͡ɪ.do͡ʊ/, /tə.ˈmɑ.to͡ʊ/) ---
+or even within the same word (like the letter "a" in "application") ---
+IPA symbols represent a single sound, or phoneme;
+the mid-central vowel, "ə" (a.k.a "schwa")
+sounds the same whether its part of
+an English word or a Japanese word or nonsensical babbling.
+
+Working with IPA on computers has pretty much always been a PITA,
+for three reasons:
+
+**Incomplete Text Encoding**
+: Until Unicode version 6.1,
+some IPA symbols didn't have a specified code point,
+forcing linguists to either use a similar-looking character
+or define _ad hoc_ encodings within a
+[Private Use Area](https://en.wikipedia.org/wiki/Private_Use_Areas).
+
+**Limited Font Support**
+: It's one thing to have a specified code point.
+Having a font that can shape, or render that code point correctly
+is another matter entirely.
+
+**Lack of Input Methods**
+: Just because the computer can represent and render a character
+doesn't mean that you, as a user,
+can produce that character in the first place.
+Typing on a QWERTY keyboard,
+we take for granted being able to type the j key
+to produce the letter "j".
+But what if you wanted to type "[ʝ](https://en.wikipedia.org/wiki/Voiced_palatal_fricative)"?
+
+For all too many people,
+the answer is _"Google and copy-paste"_.
+
+{% info %}
+For the correct answer for producing characters
+that you can't or don't know how to type,
+see our article about the
+[macOS Character Viewer](https://nshipster.com/character-viewer/).
+{% endinfo %}
+
+Fortunately,
+the first and second of these three challenges are no longer an issue
+on modern operating systems:
+Unicode provides code points for all of the
+[IPA characters](https://en.wikipedia.org/wiki/Phonetic_symbols_in_Unicode),
+and most platforms natively render them all
+without having to install a custom font.
+However, the problem of input methods remains an open question.
+
+SIL International hosts
+[an IPA keyboard layout](http://scripts.sil.org/cms/scripts/page.php?item_id=UniIPAKeyboard#79dbd88a)
+by Joan Wardell.
+There's also the [SuperIPA](https://www.kreativekorp.com/software/keyboards/superipa/) keyboard ---
+based on CXS, a variant of [X-SAMPA](https://en.wikipedia.org/wiki/X-SAMPA) ---
+by Kreative Korporation.
+You could also use
+[IPA Palette](https://github.com/K8TIY/IPAPalette)
+by Brian "Moses" Hall.
+
+But if none of these tick all of your boxes in terms of usability of ergonomics,
+the Accessibility Keyboard Panel Editor provides an easy way
+for anyone to hand-roll a bespoke solution:
+
+{% asset accessibility-keyboard-custom-keyboard-ipa-chart.png %}
+
+This keyboard layout was created with Panel Editor
+and is modeled after the
+[official IPA Chart](https://en.wikipedia.org/wiki/International_Phonetic_Alphabet_chart),
+with symbols arranged by place and manner of articulation.
+It's not nearly as efficient as any of the aforementioned keyboard layouts
+(nor is it as complete),
+but anyone familiar with IPA can use it for transcription immediately
+without additional training.
+
+---
+
+If you're a developer,
+there's a good chance that your next questions are
+_"What does this file format look like?"_ and
+_"Can I generate these with code rather than a GUI?"_.
+
+The short answers are "A Bundle of Property Lists", and "Yes!".
+Read on for the full breakdown:
+
+---
+
+## Inspecting the Accessibility Keyboard File Format
+
+The keyboard panel bundles themselves can be tricky to find
+if you don't know what you're looking for.
+On macOS Mojave,
+any custom panels you make can be found within the
+`~/Library/Application Support/com.apple.AssistiveControl/` directory
+in bundles with a `.ascconfig` file extension.
+
+The bundle comprises a top-level Info.plist file
+and a Resources directory containing an index of assets
+(along with any asset files, like button images)
+as well as a file named `PanelDefinitions.plist`.
+
+```terminal
+$ tree ~/Library/Application Support/com.apple.AssistiveControl/dwellControlUserPanels1.ascconfig/
+Contents
+├── Info.plist
+└── Resources
+ ├── AssetIndex.plist
+ └── PanelDefinitions.plist
+```
+
+Opening up `PanelDefinitions.plist` reveals the inner structure
+of our custom virtual keyboard layout:
+
+{% raw %}
+
+```xml
+
+
+
+
+ Panels
+
+ USER.80B26730-BB8A-41A5-8E70-79AA134F9D0E
+
+ AssociatedApplications
+
+
+ ApplicationBundleID
+ com.apple.Notes
+ ApplicationDisplayName
+ Notes
+ ApplicationPath
+ /Applications/Notes.app
+
+
+ DisplayOrder
+ 1
+ GlidingLensSize
+ 5
+ HasTransientPosition
+
+ HideHome
+
+ HideMinimize
+
+ HidePanelAdjustments
+
+ HideSwitchDock
+
+ HideSwitchDockContextualButtons
+
+ HideTitlebar
+
+ ID
+ USER.80B26730-BB8A-41A5-8E70-79AA134F9D0E
+ Name
+ Keyboard - IPA
+ PanelObjects
+
+ <#...#>
+
+
+
+
+```
+
+{% endraw %}
+
+The `PanelObjects` key is associated with an array of dictionaries,
+each representing a single button.
+Fortunately, he majority of the key names are self-explanatory:
+
+{% raw %}
+
+```xml
+
+ ButtonType
+ 0
+ DisplayColor
+ 0.145 0.145 0.145 1.000
+ DisplayImageResourceIsTemplate
+
+ DisplayText
+ p
+ DisplayTextLocation
+ DisplayInside
+ DisplayTextPosition
+ Center
+ DisplayTheme
+ DisplayThemeDefault
+ FontSize
+ 20
+ ID
+ Button.7B824E7E-9AB8-42E3-BA7B-B56924B45554
+ PanelObjectType
+ Button
+ Rect
+ {{0, 5}, {35, 35}}
+ Actions
+
+
+ ActionParam
+
+ CharString
+ p
+ isStickyKey
+
+
+ ActionRecordedOffset
+ 0.0
+ ActionType
+ ActionPressKeyCharSequence
+ ID
+ Action.0AE7D5DD-C588-40FA-942E-89E25FD81EEA
+
+
+
+```
+
+{% endraw %}
+
+The takeaway from looking at the file format is that
+it'd be very easy to generate Accessibility Keyboard panels in code,
+rather than using the Panel Editor app.
+(In fact, we used find-and-replace to
+bulk resize the buttons in the IPA keyboard,
+a task that would have otherwise taken 100⨉ longer).
+
+## Additional Use Cases
+
+There are dozens of scripts comprising hundreds of characters
+that lack a dedicated keyboard layout.
+And the macOS Accessibility Keyboard offers a wonderful, built-in solution
+for producing these characters.
+
+But what else could you do with this technology,
+now that you know it exists?
+
+Here are a few ideas for you to consider:
+
+### Templating Frequent Communications
+
+Do you find yourself writing the same things over and over again
+in emails or GitHub Issues?
+Create a custom, virtual keyboard to summon [boilerplate](/swift-gyb/)
+with the click of your mouse or the tap of your trackpad.
+
+{% info %}
+
+You can configure the Accessibility Keyboard to only show up
+for a particular application.
+
+{% endinfo %}
+
+### Generative Text
+
+The Accessibility Keyboard isn't limited to canned responses.
+Thanks to its AppleScript integration,
+you can populate text dynamically from virtually any source.
+
+For example,
+you could create a Fortune button
+that inserts a (pseudo)random entry from the
+[`fortune`](https://en.wikipedia.org/wiki/Fortune_%28Unix%29) program,
+with the following AppleScript:
+
+```applescript
+set fortune to do shell script "/usr/local/bin/fortune"
+set the clipboard to fortune as text
+delay 0.01
+tell application "System Events" to tell (name of application processes whose frontmost is true) to keystroke "v" using command down
+```
+
+Obligatory `fortune` output:
+
+
+If your bread is stale, make toast.
+
+
+### Sound Board
+
+Do you aspire to be a
+drive-time radio DJ
+live streamer?
+Use the Accessibility Keyboard to trigger funny sound effects at will
+to delight your throng of fans.
+
+{% asset accessibility-keyboard-panel-editor-sounds.png %}
+
+```applescript
+do shell script "afplay /System/Sounds/Sosumi.aiff"
+```
+
+### World Domination
+
+AppleScript gives you the closest thing to complete,
+programmatic access to the entire system.
+
+Set a button to kick off a build of your app,
+or send a message on Slack,
+or turn on the lights in your house,
+or play your theme song!
+
+---
+
+The Accessibility Keyboard serves as a powerful, built-in, and omnipresent
+interface to whatever functionality you desire ---
+without going through all the trouble of building an app.
+
+Because, if you think about it,
+is there any real difference between
+the j key on your keyboard
+and a hypothetical Party button on a virtual keyboard?
+
+The strong connection between
+the word "computer" and typewriter keyboards
+is merely a historical one.
+The rise of smartphones and smartwatches help illustrate this.
+Any distinction between
+the computers in your hand, on your wrist, or on your desk
+is ultimately insignificant.
+All computers are the same;
+they're all force multipliers.
+
+Once you separate "desktop" computers from the shape of their primary interface,
+you can start to fully appreciate the full capabilities
+of what's at our fingertips.
diff --git a/2019-08-26-identifiable.md b/2019-08-26-identifiable.md
new file mode 100644
index 00000000..200716fa
--- /dev/null
+++ b/2019-08-26-identifiable.md
@@ -0,0 +1,560 @@
+---
+title: Identifiable
+author: Mattt
+category: Swift
+excerpt: >-
+ Swift 5.1 gives us yet another occasion to ponder ontological questions
+ and weigh in the relative merits of various built-in types
+ as stable identifiers.
+status:
+ swift: 5.1
+---
+
+_What constitutes the identity of an object?_
+
+Philosophers have contemplated such matters throughout the ages.
+Whether it's to do with
+[reconstructed seafaring vessels from antiquity](https://en.wikipedia.org/wiki/Ship_of_Theseus)
+or [spacefaring vessels from science fiction](https://scifi.stackexchange.com/questions/13437/in-star-trek-does-the-original-die-in-teleportation),
+questions of Ontology reveal our perception and judgment to be
+much less certain than we'd like to believe.
+
+Our humble publication has frequented this topic with some regularity,
+whether it was attempting to make sense of
+[equality in Objective-C](/equality/)
+or appreciating the much clearer semantics of Swift
+vis-à-vis the [`Equatable` protocol](/equatable-and-comparable/).
+
+Swift 5.1 gives us yet another occasion to ponder this old chestnut
+by virtue of the new `Identifiable` protocol.
+We'll discuss the noumenon of this phenomenal addition to the standard library,
+and help you identify opportunities to
+realize its potential in your own projects.
+
+But let's dispense with the navel gazing and
+jump right into some substance:
+
+---
+
+Swift 5.1 adds the `Identifiable` protocol to the standard library,
+declared as follows:
+
+```swift
+protocol Identifiable {
+ associatedtype ID: Hashable
+ var id: ID { get }
+}
+```
+
+Values of types adopting the `Identifiable` protocol
+provide a stable identifier for the entities they represent.
+
+For example,
+a `Parcel` object may use the `id` property requirement
+to track the package en route to its final destination.
+No matter where the package goes,
+it can always be looked up by its `id`:
+
+```swift
+import CoreLocation
+
+struct Parcel: Identifiable {
+ let id: String
+ var location: CLPlacemark?
+}
+```
+
+{% info %}
+
+Our first introduction to the `Identifiable` protocol
+actually came by way of SwiftUI;
+it's
+[thanks to the community](https://forums.swift.org/t/move-swiftuis-identifiable-protocol-and-related-types-into-the-standard-library/25713)
+that the type was brought into the fold of the standard library.
+
+Though
+[as evidenced by GitHub search results](https://github.com/search?q=%22protocol+Identifiable%22&type=Code),
+many of us were already working with `Identifiable` protocols of similar design...
+which prompts the question:
+_When was the `Identifiable` protocol really introduced?_ 🤔
+
+{% endinfo %}
+
+The Swift Evolution proposal for `Identifiable`,
+[SE-0261](https://github.com/apple/swift-evolution/blob/master/proposals/0261-identifiable.md),
+was kept small and focused in order to be incorporated quickly.
+If you were to ask,
+_"What do you actually get by conforming to `Identifiable`?"_,
+the answer right now is _"Not much."_
+As mentioned in the [future directions](https://github.com/apple/swift-evolution/blob/master/proposals/0261-identifiable.md#future-directions),
+conformance to `Identifiable` has the potential to unlock
+simpler and/or more optimized versions of other functionality,
+such as the new [ordered collection diffing](https://github.com/apple/swift-evolution/blob/master/proposals/0240-ordered-collection-diffing.md) APIs.
+
+But the question remains:
+_"Why bother conforming to `Identifiable`?"_
+
+The functionality you get from adopting `Identifiable` is primarily semantic,
+and require some more explanation.
+It's sort of like asking,
+_"Why bother conforming to `Equatable`?"_
+
+And actually, that's not a bad place to start.
+Let's talk first about `Equatable` and its relation to `Identifiable`:
+
+## Identifiable vs. Equatable
+
+`Identifiable` distinguishes the identity of an entity from its state.
+
+A parcel from our previous example
+will change locations frequently as it travels to its recipient.
+Yet a normal equality check (`==`)
+would fail the moment it leaves its sender:
+
+```swift
+extension Parcel: Equatable {}
+
+var specialDelivery = Parcel(id: "123456789012")
+specialDelivery.location = CLPlacemark(
+ location: CLLocation(latitude: 37.3327,
+ longitude: -122.0053),
+ name: "Cupertino, CA"
+ )
+
+specialDelivery == Parcel(id: "123456789012") // false
+specialDelivery.id == Parcel(id: "123456789012").id // true
+```
+
+While this is an expected outcome from a small, contrived example,
+the very same behavior can lead to confusing results further down the stack,
+where you're not as clear about how different parts work with one another.
+
+```swift
+var trackedPackages: Set = <#...#>
+trackedPackages.contains(Parcel(id: "123456789012")) // false (?)
+```
+
+On the subject of `Set`,
+let's take a moment to talk about the `Hashable` protocol.
+
+## Identifiable vs. Hashable
+
+In [our article about `Hashable`](/hashable/),
+we described how `Set` and `Dictionary` use a calculated hash value
+to provide constant-time (`O(1)`) access to elements in a collection.
+Although the hash value used to bucket collection elements
+may bear a passing resemblance to identifiers,
+`Hashable` and `Identifiable` have some important distinctions
+in their underlying semantics:
+
+- Unlike identifiers,
+ hash values are typically _state-dependent_,
+ changing when an object is mutated.
+- Identifiers are _stable_ across launches,
+ whereas hash values are calculated by randomly generated hash seeds,
+ making them _unstable_ between launches.
+- Identifiers are _unique_,
+ whereas hash values may _collide_,
+ requiring additional equality checks when fetched from a collection.
+- Identifiers can be _meaningful_,
+ whereas hash values are _chaotic_
+ by virtue of their hashing functions.
+
+In short,
+hash values are similar to
+but no replacement for identifiers.
+
+_So what makes for a good identifier, anyway?_
+
+## Choosing ID Types
+
+Aside from conforming to `Hashable`,
+`Identifiable` doesn't make any other demands of
+its associated `ID` type requirement.
+So what are some good candidates?
+
+If you're limited to only what's available in the Swift standard library,
+your best options are `Int` and `String`.
+Include Foundation,
+and you expand your options with `UUID` and `URL`.
+Each has its own strengths and weaknesses as identifiers,
+and can be more or less suited to a particular situation:
+
+### Int as ID
+
+The great thing about using integers as identifiers
+is that (at least on 64-bit systems),
+you're unlikely to run out of them anytime soon.
+
+Most systems that use integers to identify records
+assign them in an auto-incrementing manner,
+such that each new ID is 1 more than the last one.
+Here's a simple example of how you can do this in Swift:
+
+```swift
+struct Widget: Identifiable {
+ private static var idSequence = sequence(first: 1, next: {$0 + 1})
+
+ let id: Int
+
+ init?() {
+ guard let id = Widget.idSequence.next() else { return nil}
+ self.id = id
+ }
+}
+
+Widget()?.id // 1
+Widget()?.id // 2
+Widget()?.id // 3
+```
+
+If you wanted to guarantee uniqueness across launches,
+you might instead initialize the sequence with a value
+read from a persistent store like `UserDefaults`.
+And if you found yourself using this pattern extensively,
+you might consider factoring everything into a self-contained
+[property wrapper](/propertywrapper/).
+
+Monotonically increasing sequences have a lot of benefits,
+and they're easy to implement.
+
+This kind of approach can provide unique identifiers for records,
+but only within the scope of the device on which the program is being run
+(and even then, we're glossing over a lot with respect to concurrency
+and shared mutable state).
+
+If you want to ensure that an identifier is unique across
+_every_ device that's running your app, then
+congratulations ---you've hit
+[a fundamental problem in computer science](https://en.wikipedia.org/wiki/Consensus_%28computer_science%29).
+But before you start in on
+[vector clocks](https://en.wikipedia.org/wiki/Vector_clock) and
+[consensus algorithms](https://en.wikipedia.org/wiki/Consensus_algorithm),
+you'll be relieved to know that there's a
+much simpler solution:
+UUIDs.
+
+{% error %}
+
+Insofar as this is a concern for your app,
+don't expose serial identifiers to end users.
+Not only do you inadvertently disclose information about your system
+(_"How many customers are there? Just sign up and check the user ID!"_),
+but you open the door for unauthorized parties to
+enumerate all of the records in your system
+(_"Just start at id = 1 and keep incrementing until a record doesn't exist"_).
+
+Granted, this is more of a concern for web apps,
+which often use primary keys in URLs,
+but it's something to be aware of nonetheless.
+
+{% enderror %}
+
+### UUID as ID
+
+[UUIDs](https://en.wikipedia.org/wiki/Universally_unique_identifier), or
+universally unique identifiers,
+(mostly) sidestep the problem of consensus with probability.
+Each UUID stores 128 bits ---
+minus 6 or 7 format bits, depending on the
+[version](https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions) ---
+which, when randomly generated,
+make the chances of collision,
+or two UUIDs being generated with the same value,
+_astronomically_ small.
+
+[As discussed in a previous article](/uuid-udid-unique-identifier/),
+Foundation provides a built-in implementation of (version-4) UUIDs
+by way of the
+[`UUID` type](https://developer.apple.com/documentation/foundation/uuid).
+Thus making adoption to `Identifiable` with UUIDs trivial:
+
+```swift
+import Foundation
+
+struct Gadget: Identifiable {
+ let id = UUID()
+}
+
+Gadget().id // 584FB4BA-0C1D-4107-9EE5-C555501F2077
+Gadget().id // C9FECDCC-37B3-4AEE-A514-64F9F53E74BA
+```
+
+Beyond minor ergonomic and cosmetic issues,
+`UUID` serves as an excellent alternative to `Int`
+for generated identifiers.
+
+However,
+your model may already be uniquely identified by a value,
+thereby obviating the need to generate a new one.
+Under such circumstances,
+that value is likely to be a `String`.
+
+{% info %}
+On macOS,
+you can generate a random UUID from Terminal
+with the built-in `uuidgen` command:
+
+```terminal
+$ uuidgen
+39C884B8-0A11-4B4F-9107-3AB909324DBA
+```
+
+{% endinfo %}
+
+### String as ID
+
+We use strings as identifiers all the time,
+whether it takes the form of a username or a checksum or a translation key
+or something else entirely.
+
+The main drawback to this approach is that,
+thanks to The Unicode® Standard,
+strings encode thousands of years of written human communication.
+So you'll need a strategy for handling identifiers like
+"⽜", "𐂌", "", and "🐮"
+...and that's to say nothing of the more pedestrian concerns,
+like leading and trailing whitespace and case-sensitivity!
+
+Normalization is the key to successfully using strings as identifiers.
+The easiest place to do this is in the initializer,
+but, again, if you find yourself repeating this code over and over,
+[property wrappers](/propertywrapper/) can help you here, too.
+
+```swift
+import Foundation
+
+fileprivate extension String {
+ var nonEmpty: String? { isEmpty ? nil : self }
+}
+
+struct Whosit: Identifiable {
+ let id: String
+
+ init?(id: String) {
+ guard let id = id.trimmingCharacters(in: CharacterSet.letters.inverted)
+ .lowercased()
+ .nonEmpty
+ else {
+ return nil
+ }
+
+ self.id = id
+ }
+}
+
+Whosit(id: "Cow")?.id // cow
+Whosit(id: "--- cow ---")?.id // cow
+Whosit(id: "🐮") // nil
+```
+
+### URL as ID
+
+URLs (or URIs if you want to be pedantic)
+are arguably the most ubiquitous kind of identifier
+among all of the ones described in this article.
+Every day, billions of people around the world use URLs
+as a way to point to a particular part of the internet.
+So URLs a natural choice for an `id` value
+if your models already include them.
+
+URLs look like strings,
+but they use [syntax](https://tools.ietf.org/html/rfc3986)
+to encode multiple components,
+like scheme, authority, path, query, and fragment.
+Although these formatting rules dispense with much of the invalid input
+you might otherwise have to consider for strings,
+they still share many of their complexities ---
+with a few new ones, just for fun.
+
+The essential problem is that
+equivalent URLs may not be equal.
+Intrinsic, syntactic details like
+case sensitivity,
+the presence or absence of a trailing slash (`/`),
+and the order of query components
+all affect equality comparison.
+So do extrinsic, semantic concerns like
+a server's policy to upgrade `http` to `https`,
+redirect from `www` to the apex domain,
+or replace an IP address with a
+which might cause different URLs to resolve to the same webpage.
+
+```swift
+URL(string: "https://nshipster.com/?a=1&b=2")! ==
+ URL(string: "http://www.NSHipster.com?b=2&a=1")! // false
+
+try! Data(contentsOf: URL(string: "https://nshipster.com?a=1&b=2")!) ==
+ Data(contentsOf: URL(string: "http://www.NSHipster.com?b=2&a=1")!) // true
+```
+
+{% info %}
+Many of the same concerns apply to file URLs as well,
+which have the additional prevailing concern of resolving relative paths.
+{% endinfo %}
+
+If your model gets identifier URLs for records from a trusted source,
+then you may take URL equality as an article of faith;
+if you regard the server as the ultimate source of truth,
+it's often best to follow their lead.
+
+But if you're working with URLs in any other capacity,
+you'll want to employ some combination of
+[URL normalizations](https://en.wikipedia.org/wiki/URL_normalization)
+before using them as an identifier.
+
+Unfortunately, the Foundation framework doesn't provide
+a single, suitable API for URL canonicalization,
+but `URL` and `URLComponents` provide enough on their own
+to let you roll your own
+(_though we'll leave that as an exercise for the reader_):
+
+```swift
+import Foundation
+
+fileprivate extension URL {
+ var normalizedString: String { <#...#> }
+}
+
+struct Whatsit: Identifiable {
+ let url: URL
+ var id: { url.normalizedString }
+}
+
+Whatsit(url: "https://example.com/123").id // example.com/123
+Whatsit(id: "http://Example.com/123/").id // example.com/123
+```
+
+## Creating Custom Identifier ID Types
+
+`UUID` and `URL` both look like strings,
+but they use syntax rules to encode information in a structured way.
+And depending on your app's particular domain,
+you may find other structured data types that
+would make for a suitable identifier.
+
+Thanks to the flexible design of the `Identifiable` protocol,
+there's nothing to stop you from implementing your own `ID` type.
+
+For example,
+if you're working in a retail space,
+you might create or repurpose an existing
+[`UPC`](https://en.wikipedia.org/wiki/Universal_Product_Code) type
+to serve as an identifier:
+
+```swift
+struct UPC: Hashable {
+ var digits: String
+ <#...#>
+}
+
+struct Product: Identifiable {
+ let id: UPC
+ var name: String
+ var price: Decimal
+}
+```
+
+## Three Forms of ID Requirements
+
+As `Identifiable` makes its way into codebases,
+you're likely to see it used in one of three different ways:
+
+The newer the code,
+the more likely it will be for `id` to be a stored property ---
+most often this will be declared as a constant (that is, with `let`):
+
+```swift
+import Foundation
+
+// Style 1: id requirement fulfilled by stored property
+struct Product: Identifiable {
+ let id: UUID
+}
+```
+
+Older code that adopts `Identifiable`,
+by contrast,
+will most likely satisfy the `id` requirement
+with a computed property
+that returns an existing value to serve as a stable identifier.
+In this way,
+conformance to the new protocol is purely additive,
+and can be done in an extension:
+
+```swift
+import Foundation
+
+struct Product {
+ var uuid: UUID
+}
+
+// Style 2: id requirement fulfilled by computed property
+extension Product: Identifiable {
+ var id { uuid }
+}
+```
+
+If by coincidence the existing class or structure already has an `id` property,
+it can add conformance by simply declaring it in an extension
+_(assuming that the property type conforms to `Hashable`)_.
+
+```swift
+import Foundation
+
+struct Product {
+ var id: UUID
+}
+
+// Style 3: id requirement fulfilled by existing property
+extension Product: Identifiable {}
+```
+
+No matter which way you choose,
+you should find adopting `Identifiable` in a new or existing codebase
+to be straightforward and noninvasive.
+
+---
+
+As we've said [time](/numericcast/) and [again](/never/),
+often it's the smallest additions to the language and standard library
+that have the biggest impact on how we write code.
+(This speaks to the thoughtful,
+[protocol-oriented](https://developer.apple.com/videos/play/wwdc2015/408/)
+design of Swift's standard library.)
+
+Because what `Identifiable` does is kind of amazing:
+**it extends reference semantics to value types**.
+
+When you think about it,
+reference types and value types differ not in what information they encode,
+but rather how we treat them.
+
+For reference types,
+the stable identifier is the address in memory
+in which the object resides.
+This fact can be plainly observed
+by the default protocol implementation of `id` for `AnyObject` types:
+
+```swift
+extension Identifiable where Self: AnyObject {
+ var id: ObjectIdentifier {
+ return ObjectIdentifier(self)
+ }
+}
+```
+
+Ever since Swift first came onto the scene,
+the popular fashion has been to eschew all reference types for value types.
+And this neophilic tendency has only intensified
+with the announcement of SwiftUI.
+But taking such a hard-line approach makes a value judgment
+of something better understood to be a difference in outlook.
+
+It's no coincidence that much of the terminology of programming
+is shared by mathematics and philosophy.
+As developers, our work is to construct logical universes, after all.
+And in doing so,
+we're regularly tasked with reconciling our own mental models
+against that of every other abstraction we encounter down the stack ---
+down to the very way that we understand electricity and magnetism to work.
diff --git a/2019-09-02-dark-mode.md b/2019-09-02-dark-mode.md
new file mode 100644
index 00000000..45661137
--- /dev/null
+++ b/2019-09-02-dark-mode.md
@@ -0,0 +1,957 @@
+---
+title: Dark Mode on iOS 13
+author: Mattt
+category: Miscellaneous
+excerpt: >-
+ After waiting for what felt like an eternity (but was only, like, a year),
+ Dark Mode is now finally coming to the iPhone and iPad iOS 13.
+ But will your app be ready on launch day?
+status:
+ swift: 5.1
+---
+
+Today is Labor Day in the United States
+(and Labour Day in Canada),
+a day to celebrate the achievement of the workers
+who organized to bring about fair and safe conditions for employees ---
+protections that serve as the foundation for the modern workplace.
+
+{% info %}
+
+By contrast,
+much of the rest of the world
+instead observe
+[International Worker's Day](https://en.wikipedia.org/wiki/International_Workers%27_Day) on May 1st;
+[the resulting map](https://en.wikipedia.org/wiki/Labour_Day#/media/File:Observance_of_International_Workers'_Day_-_Revised.svg)
+is reminiscent of some other
+[world](https://en.wikipedia.org/wiki/Metrication#/media/File:Metrication.svg)
+[maps](https://en.wikipedia.org/wiki/Fahrenheit#/media/File:Countries_that_use_Fahrenheit.svg).
+
+{% endinfo %}
+
+Labor Day is also the unofficial end of summer;
+the long weekend acting as a buffer between
+the lemonades, sunburns, and trashy romance novels of August and
+the pumpkin spice lattes, flu shots, and textbooks of September.
+
+However,
+for the stereotypical tech worker,
+who likes the heat of summer about as much as
+the concept of "work/life balance",
+Labor Day frequently serves as something else entirely:
+_a wake-up call_.
+That,
+after a lazy summer of ideation and experimentation,
+it's once again time to launch new products and ship new features.
+And if you're an app developer specifically,
+you may know today as,
+_"Oh-🤬-it's-September-and-I-still-haven't-implemented-Dark-Mode-in-my-app"_ day.
+
+We're dedicating this week's article to anyone out there
+who's celebrating this esteemed holiday,
+whether contemporaneously or in the intervening days until iOS 13 goes GM.
+We hope that a few quick tips can help shed light
+against the shade of your looming deadline.
+
+---
+
+{% asset dark-mode-apple-banner.png %}
+
+Dark Mode is an appearance preference
+that tells the system and participating apps to adopt darker color palettes.
+Whereas an app may display dark text on a light background by default,
+it may instead show white text on a dark background.
+
+Last year,
+Dark Mode was _the_ killer feature of macOS 10.14 Mojave,
+and its contemporaneous launch with Safari 12
+rippled throughout the World-Wide Web,
+gaining steady adoption among websites
+([like yours truly](https://twitter.com/nshipster/status/1055174887168339968?lang=en))
+and
+[other browsers](https://caniuse.com/#search=prefers-color-scheme).
+
+After waiting for what felt like an eternity
+(but was only, like, a year),
+Dark Mode is now _finally_ coming to the iPhone and iPad iOS 13.
+No longer will we have to tinker with
+[Display Accommodations](https://support.apple.com/en-us/HT207025)
+to limit our light pollution when browsing Reddit late at night.
+
+## Adopting Dark Mode on iOS
+
+Apple's done a great job designing a flexible, convenient API
+and providing [excellent documentation to go with it](https://developer.apple.com/documentation/appkit/supporting_dark_mode_in_your_interface/choosing_a_specific_interface_style_for_your_ios_app).
+
+Of course, the challenge with Apple technologies
+is that short of telling you that "you're holding it wrong",
+they'll rarely acknowledge the _existence_ of prior art or alternatives,
+let alone provide a transition document that in any way resembles
+how everyone was doing things before we got an officially-sanctioned API.
+(_Then again, can you really blame them?_)
+
+If you were following 100% of Apple's guidance to the letter,
+you'd barely have to change a line or code
+to get your app ready for
+[next week's special event](https://www.apple.com/apple-events/september-2019/).
+But most apps are built on solutions we built for ourselves
+to bridge the gaps in the SDK,
+and it may not be clear how to get on the new
+[happy path](https://en.wikipedia.org/wiki/Happy_path)
+from there.
+
+Apple's guidance for adopting Dark Mode is fantastic for new projects
+but doesn't quite hit all of the points you should be aware of
+when preparing your existing app for iOS 13.
+So without further ado,
+here's 6 action items for how to get your app ready for Dark Mode.
+
+---
+
+### #Cancel Color Literals
+
+In Xcode,
+a color literal
+is code with the prefix `#colorLiteral`
+that is rendered as a color swatch in the editor.
+For example,
+the code `#colorLiteral(red: 1, green: 0.67, blue: 0, alpha: 1)`
+is rendered in Xcode as .
+Color literals can be drag-and-dropped from
+the Media Browser in Xcode 10,
+which has been consolidated with Snippets into the new Library panel in Xcode 11.
+
+Both color literals and their cousin, image literals,
+were introduced in support of Xcode Playgrounds.
+But don't let their appearance fool you:
+neither have a place in your app's codebase.
+
+{% error %}
+
+Image literals could almost immediately be ruled out
+due to their flakey rendering,
+but it's only with the advent Dark Mode
+that we have a strong compelling reason to give them the boot:
+**color literals don't support dynamic / named colors**.
+
+Attempting to drag a named color into the editor
+results in two adjacent `#colorLiteral` expressions ---
+an invalid statement in Swift.
+If you were to set one as the color property for a view mistakenly,
+it wouldn't change its appearance for dark or light mode.
+
+{% enderror %}
+
+Dark Mode or not,
+you can replace all usage of color literals throughout your codebase
+from the command line:
+
+```terminal
+$ find . -name '*.swift' \
+ -exec sed -i '' -E 's/#colorLiteral\(red: (.*), green: (.*), blue: (.*), alpha: (.*)\)/UIColor(red: \1, green: \2, blue: \3, alpha: \4)/' {} \;
+```
+
+But before you do,
+you might as well do one better
+and replace it with something that will stand the test of time.
+
+### Nix UIColor Hexadecimal Initializers
+
+Among the most common extensions you'll find in a
+Swiss Army Knife-style CocoaPod
+is a category on `UIColor` that initializes from a hexadecimal string.
+Something along the lines of this:
+
+```swift
+import SwiftySwiftColorSwift
+let orange = UIColor(hex: "#FB8C00") // 👎
+```
+
+Setting aside any qualms about how they're typically employed and implemented,
+you'd be well-advised to have these go the way of color literals
+for the same reasons we described in the previous section.
+
+But worry not!
+You'll still have a way to define colors
+using those hex codes that your designer sent over,
+as we'll see in our discussion of [named colors](#color-assets).
+
+### Find & Replace Fixed Colors
+
+`UIColor` defines several class properties
+that return colors by their common name.
+These properties are problematic in iOS 13,
+because they don't automatically adjust for light or dark appearance.
+For example,
+setting a label's color to `.black` looks fine
+against the default `UITableViewCell` background,
+but it's illegible when that background becomes black
+when Dark Mode is enabled.
+
+To make your app ready for Dark Mode on iOS 13,
+you'll most likely want to replace any instance of the following
+`UIColor` class properties:
+
+
+
+Hopefully you aren't using the built-in
+ROYGBIV
+`UIColor` constants for much other than occasional layout debugging,
+but chances you'll probably find a few instances of `.black` or `.white`
+peppered throughout your codebase somewhere.
+
+In any case,
+the easiest change to support Dark Mode would be to
+replace any of the aforementioned fixed color properties with
+the corresponding `system`-prefixed adaptable color below:
+
+{::nomarkdown}
+
+{% asset articles/dark-mode.css %}
+
+
+
+
+
Name
+
API
+
Light
+
Dark
+
+
+
Default
+
Accessible
+
Default
+
Accessible
+
+
+
+
+
+
+
Red
+
systemRed
+
+
+
+
+
+
+
+
+
Orange
+
systemOrange
+
+
+
+
+
+
+
+
+
Yellow
+
systemYellow
+
+
+
+
+
+
+
+
+
Green
+
systemGreen
+
+
+
+
+
+
+
+
+
Teal
+
systemTeal
+
+
+
+
+
+
+
+
+
Blue
+
systemBlue
+
+
+
+
+
+
+
+
+
Indigo
+
systemIndigo
+
+
+
+
+
+
+
+
+
Purple
+
systemPurple
+
+
+
+
+
+
+
+
+
Pink
+
systemPink
+
+
+
+
+
+
+
+
+
+
+
+
+
Gray
+
systemGray
+
+
+
+
+
+
+
+
+
Gray (2)
+
systemGray2
+
+
+
+
+
+
+
+
+
Gray (3)
+
systemGray3
+
+
+
+
+
+
+
+
+
Gray (4)
+
systemGray4
+
+
+
+
+
+
+
+
+
Gray (5)
+
systemGray5
+
+
+
+
+
+
+
+
+
Gray (6)
+
systemGray6
+
+
+
+
+
+
+
+
+{:/}
+
+You may notice that this table doesn't provide direct correspondences for
+black or white (or brown, but disregard that for now).
+
+Black and white don't have adaptive colors
+because their names would cease to be descriptive in Dark Mode;
+if `.systemBlack` existed, it'd pretty much have to be `.white`
+to be visible in a dark color pallet.
+
+Which gets to a deeper point about color management in an era of Dark Mode...
+
+### Use Semantic Colors
+
+The best way to ensure consistent rendering of your UI
+on any device in any appearance mode
+is to use semantic colors,
+named according to their function rather than appearance.
+
+Similar to how [Dynamic Type](https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/typography/)
+uses semantic tags like "Headline" and "Body" to
+automatically set the most suitable font for the user's display preferences,
+semantic colors ---
+or what Apple's calling
+[UI Element Colors](https://developer.apple.com/documentation/uikit/uicolor/ui_element_colors) ---
+provide future-proof behavior for your views and controls.
+
+When styling your component,
+set the color to the closest `UIColor` class property below:
+
+
+
+### Upgrade Homegrown Semantic Color Palettes
+
+If you've given any thought to color management in your app
+you'll have likely landed on some form of the following strategy,
+whereby you define semantic colors according to fixed colors
+within a namespace or in an extension to `UIColor`.
+
+For example,
+the following example shows how an app might define
+`UIColor` constants from the
+[Material UI color system](https://material.io/design/color/#color-usage-palettes)
+and reference them from semantic `UIColor` class properties:
+
+```swift
+import UIKit
+
+extension UIColor {
+ static var customAccent: UIColor { return MaterialUI.red500 }
+ <#...#>
+}
+
+fileprivate enum MaterialUI {
+ static let orange600 = UIColor(red: 0xFB / 0xFF,
+ green: 0x8C / 0xFF,
+ blue: 0x00 / 0xFF,
+ alpha: 1) // #FB8C00
+ <#...#>
+}
+```
+
+If your app uses a pattern like this,
+you can make it Dark Mode compatible
+using the new
+[`init(dynamicProvider:)`](https://developer.apple.com/documentation/uikit/uicolor/3238041-init)
+`UIColor` initializer in iOS 13 like so:
+
+```swift
+import UIKit
+
+extension UIColor
+ static var customAccent: UIColor {
+ if #available(iOS 13, *) {
+ return UIColor { (traitCollection: UITraitCollection) -> UIColor in
+ if traitCollection.userInterfaceStyle == .dark {
+ return MaterialUI.orange300
+ } else {
+ return MaterialUI.orange600
+ }
+ }
+ } else {
+ return MaterialUI.orange600
+ }
+ }
+}
+```
+
+Nothing about the fixed Material UI color constants has to change
+with this approach.
+Instead, the semantic color property `customAccent` provides a dynamic color
+that uses the color most appropriate for the current rendering context.
+When Dark Mode is enabled,
+a lighter orange is used to contrast against the darker color palette;
+otherwise, the behavior is unchanged from the original implementation.
+
+The extra `#available` check creates some bloat in the implementation,
+but it's a small price to pay for the flexibility this approach provides.
+
+{% info %}
+
+For bonus points,
+you can extend this approach to
+support both light and dark modes in default and high contrast modes:
+
+```swift
+// iOS >= 13
+UIColor { (traitCollection: UITraitCollection) -> UIColor in
+ switch(traitCollection.userInterfaceStyle,
+ traitCollection.accessibilityContrast)
+ {
+ case (.dark, .high): return MaterialUI.orangeA200
+ case (.dark, _): return MaterialUI.orange300
+ case (_, .high): return MaterialUI.orangeA700
+ default: return MaterialUI.orange600
+ }
+}
+```
+
+{% endinfo %}
+
+Unfortunately,
+there's one crucial shortcoming to using color properties in this way:
+**they can't be referenced from Interface Builder**.
+
+If your app uses either Storyboards or XIBs,
+the best approach is to use color assets.
+
+### Manage Colors with an Asset Catalog
+
+Color assets let you manage colors in an Xcode Asset Catalog
+in the same way that you do for
+images, [data](/nsdataasset/), or other resources.
+
+
+
+
+
+
+To create a color set,
+open an Asset Catalog in your Xcode project,
+click the + button on the bottom left-hand corner,
+and select "New Color Set".
+In the Attributes inspector,
+select "Any, Dark" appearance.
+Colors are frequently expressed in the form ("#RRGGBB");
+you can enter colors in this form by
+selecting "8-bit Hexadecimal" from the "Input Method" drop-down.
+
+
+
+
+
+
+Here,
+we've done the same as before,
+except instead of defining fixed `UIColor` constants like `orange300` in code,
+we set those in the color set itself.
+Now when it comes time to reference the color asset
+by the existing semantic class property,
+we can use the `UIColor` named initializer:
+
+```swift
+extension UIColor {
+ @available(iOS 11, *)
+ var customAccent: UIColor! {
+ return UIColor(named: "Accent")
+ }
+}
+```
+
+{% info %}
+
+A single app can have multiple Asset Catalogs.
+Consider creating a separate one just for your color assets.
+
+{% endinfo %}
+
+Your opinion about color assets will largely be informed by
+your preference or dispreference towards specialized Xcode document editors.
+Some folks like to have everything spelled out in code,
+while others appreciate the affordances provided by a bespoke UI
+like the one provided by Xcode for color sets.
+
+In fact, your opinion of color assets
+is probably concomitant with your feelings about Storyboards ---
+which is convenient,
+because the killer feature of color assets is that
+they can be referenced from within Interface Builder.
+_(If you're not on team IB, then you can safely skip this whole discussion.)_
+
+### Replace Instances of "Custom Color" in XIBs and Storyboards
+
+The "Custom Color" option in Interface Builder
+that brings up the macOS system-native color picker
+suffers the same problem as the color literals and fixed colors
+we talked about earlier.
+If you want your XIB-powered views to look good on iOS 13,
+you'll need to migrate to named colors.
+
+This can be done easily:
+select any component in your scene,
+and you can set its color attribute using the same, named color
+defined in your Asset Catalog.
+
+
+
+
+
+
+For a small project,
+this can be done by hand for all your screens in under an hour.
+However,
+for a larger app,
+this is a process you'll want to automate.
+
+#### XIB Anatomy
+
+Under the hood,
+XIB and Storyboard files are merely XML files like any other:
+
+```xml
+
+>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+We wouldn't want to write this from scratch by hand,
+but there's nothing too mysterious going on here.
+
+So consider what happens when you use Xcode
+to switch the custom background color of the main view to a named color:
+
+```xml
+
+>
+ />
+
+ <#...#>
+
+
+
+
+ <#...#>
+
+ <#...#>
+
+
+
+
+
+
+
+
+
+```
+
+❶
+: A new `"Named colors"` capability is added as a dependency
+for opening the document
+(Xcode uses this to determine whether it can edit files
+created by newer versions).
+
+❷
+: The `red`, `green`, `blue`, and `alpha` components on the `color` element
+were replaced by a single `name` attribute.
+
+❸
+: A corresponding `namedColor` element was added to the top-level `resources` element.
+
+Based on this understanding,
+we should know enough to make this change en masse
+with our own tooling!
+
+#### Finding All Custom Colors
+
+{% info %}
+
+The following examples use
+the [XMLStarlet](http://xmlstar.sourceforge.net/overview.php) command-line tools
+to query XIB and Storyboard files using
+[XPath](https://en.wikipedia.org/wiki/XPath).
+
+You can install it for yourself and follow along using
+[Homebrew](/homebrew/):
+
+```terminal
+$ brew install xmlstarlet
+```
+
+{% endinfo %}
+
+The first order of business when migrating your Storyboards and XIBs for Dark Mode
+is to find all of the instances of custom colors.
+You _could_ go through each by hand and click each of the visual elements...
+or you could run the following command:
+
+```terminal
+$ find . \( -name '*.xib' -or -name '*.storyboard' \) \
+ -exec echo {} \; \
+ -exec xmlstarlet sel -t \
+ -m "//color[@colorSpace='custom']" -c . -n {} \;
+
+Main.storyboard
+
+```
+
+This command prints the name of each file
+followed by each custom, unnamed `color` element it found
+(denoted by the `colorSpace="custom"` attribute).
+
+The resulting list serves as a battle plan for the next phase of attack:
+
+#### Finding Each Distinct Custom Color
+
+Apps tend to reuse a small set of colors
+across their views --- _as they should_!
+By running the following command,
+you can generate a sorted, unique'd list of
+every custom color in every XIB or Storyboard:
+
+```terminal
+$ find . \( -name '*.xib' -or -name '*.storyboard' \) \
+ -exec xmlstarlet sel -t \
+ -m "//color[@colorSpace='custom']" \
+ -v "concat( @red,' ',@green,' ',@blue,' ',@alpha)" -n {} \; \
+ | sort -u
+
+1 0.69019607839999997 0.0 1
+```
+
+Some entries may be equivalent, within a small delta of each other
+(because, you know... floating-point numbers).
+To account for account for this,
+and to transform our output into something easier to work with,
+you can write the output to a file and process it with
+a Ruby script like this one:
+
+```ruby
+colors = File.readlines('distinct_colors.txt').map do |line|
+ components = line.strip.split(/\s+/).flat_map(&:to_f)
+ red, green, blue = components[0..2].map{ |c| (c * 255).floor }
+ alpha = (components.last * 100).floor
+ [red, green, blue, alpha]
+end
+
+colors.uniq.each do |color|
+ puts "#%02X%02X%02X (%d%%)" % color
+end
+```
+
+From here,
+the final step is to map each set of
+RGBA values
+to the corresponding named color that you want to replace it with.
+
+{% info %}
+
+If your data set is large,
+or if you just don't trust your eyes to quantize these colors correctly,
+you can use a clustering algorithm to do that for you.
+
+You may be familiar with
+[_k_-means clustering](https://en.wikipedia.org/wiki/K-means_clustering),
+but that requires us to specify the target number of clusters ahead of time.
+When you don't know how many groups there are a priori,
+[DBSCAN](https://en.wikipedia.org/wiki/DBSCAN)
+can be a nice alternative.
+
+Ruby conveniently has a [gem](https://rubygems.org/gems/dbscan/versions/0.2)
+that suits our purpose\*:
+
+```ruby
+require 'dbscan'
+
+dbscan = DBSCAN(colors, epsilon: 8, min_points: 1, distance: :euclidean_distance)
+puts dbscan.results
+```
+
+_\* For best results,
+convert RGBA values to
+[a color space with perceptual uniformity](https://en.wikipedia.org/wiki/CIELUV)
+before clustering._
+
+{% endinfo %}
+
+#### Replacing Custom Colors
+
+At this point,
+we're well beyond the point where shell one-liners seem like a good idea.
+So here's a Ruby script we wrote up
+that makes all of the changes we understand to take place
+when replacing a custom color with a named color in Interface Builder:
+
+{% error %}
+
+I can't vouch for the correctness of this script,
+so use it at your own risk.
+
+{% enderror %}
+
+```ruby
+require 'nokogiri'
+
+def name_for_rgba_components(red, green, blue, alpha)
+ case format('#%02X%02X%02X%02X', red, green, blue, alpha)
+ # Return named color matching RGBA components
+ # e.g. "#F8CB00FF" => "Accent"
+ end
+end
+
+def name_for_white_and_alpha_components(white, alpha)
+ # Return named color matching white and alpha components
+ # e.g. 0.9 => "Off-White"
+end
+
+# Process each Storyboard and XIB file
+Dir['**/*.{storyboard,xib}'].each do |xib|
+ doc = Nokogiri::XML(File.read(xib))
+
+ names = []
+ # Collect each custom color and assign it a name
+ doc.xpath('//objects//color').each do |color|
+ next if color['name']
+
+ name = nil
+
+ color_space = color['colorSpace']
+ color_space = color['customColorSpace'] if color_space == 'custom'
+
+ case color_space
+ when 'sRGB', 'calibratedRGB'
+ components = color.attributes
+ .values_at('red', 'green', 'blue', 'alpha')
+ .map(&:value)
+ .map(&:to_f)
+ .map { |c| c * 255 }
+ name = name_for_rgba_components(*components)
+ when 'genericGamma22GrayColorSpace', 'calibratedWhite'
+ components = color.attributes
+ .values_at('white', 'alpha')
+ .map(&:value)
+ .map(&:to_f)
+ name = name_for_white_and_alpha_components(*components)
+ end
+
+ next unless name
+
+ named_color = doc.create_element('color',
+ key: color['key'],
+ name: name)
+ color.replace(named_color)
+ names << name
+ end
+
+ # Proceed to the next file if no named colors were found
+ next if names.empty?
+
+ # Add the named color capability as a document dependency
+ dependencies = doc.at('/document/dependencies') ||
+ doc.root.add_child(doc.create_element('dependencies'))
+ unless dependencies.at("capability[@name='Named colors']")
+ dependencies << doc.create_element('capability',
+ name: 'Named colors',
+ minToolsVersion: '9.0')
+ end
+
+ # Add each named color to the document resources
+ resources = doc.at('/document/resources') ||
+ doc.root.add_child(doc.create_element('resources'))
+ names.uniq.sort.each do |name|
+ next if resources.at("namedColor[@name='#{name}']")
+ resources << doc.create_element('namedColor', name: name)
+ end
+
+ # Save the changes
+ File.write(xib, doc.to_xml(indent: 4, encoding: 'UTF-8'))
+end
+```
+
+_\*Phew!\*_
+
+If you've been facing down a deadline for Dark Mode
+at the expense of enjoying one last hurrah of summer,
+we hope that this article was able to get you out of the office today.
+
+Its ironic that so many of us
+are eschewing our holiday weekend in a scramble to get our apps ready
+for the annual
+NMOS
+GM.
+But if it's any consolation,
+know that Apple engineers rarely get to observe
+[Memorial Day](https://en.wikipedia.org/wiki/Memorial_Day) ---
+the unofficial _start_ of summer in America ---
+in the run-up to WWDC.
diff --git a/2019-09-16-apns-device-tokens.md b/2019-09-16-apns-device-tokens.md
new file mode 100644
index 00000000..a3175bfd
--- /dev/null
+++ b/2019-09-16-apns-device-tokens.md
@@ -0,0 +1,409 @@
+---
+title: Apple Push Notification Device Tokens
+author: Mattt
+category: Cocoa
+excerpt: >-
+ Precedent plays an important role throughout software engineering.
+ So what does a change in iOS 13 to push notification registration
+ have to say about Apple's API design decisions?
+status:
+ swift: 5.1
+---
+
+In law,
+the latin phrase
+stare decisis (_"to stand by things decided"_)
+is often used to refer to the doctrine of precedent ---
+the idea that,
+when deciding a case,
+a court should look to previous decisions made
+for cases with similar facts and scenarios.
+This principle serves as a foundation of the American legal system,
+and the English common law from which it derives.
+
+For example,
+consider [_Apple v. Pepper_][apple v. pepper],
+which was argued before the Supreme Court of the United States
+in its most recent session
+and sought to settle the following question:
+
+> If Apple and its App Store constitute a monopoly,
+> can consumers sue Apple for offering apps at higher-than-competitive prices,
+> even when the prices are set by third-party developers?
+
+In its decision,
+the Court relied on precedent set in 1977
+by a case known as [_Illinois Brick_][illinois brick],
+which itself affirmed a ruling made a decade earlier
+in a case called [_Hanover Shoe_][hanover shoe].
+On its face,
+iPhones in 2010's would seem to have little to do with bricks from the 1970's
+_(aside from the [obvious connotation](https://www.theiphonewiki.com/wiki/Brick))_,
+but within the context of
+[United States antitrust law](https://en.wikipedia.org/wiki/United_States_antitrust_law),
+the connection between the two was inescapable.
+
+{% info %}
+
+Of course, there are _many_ other cases
+that offer simpler and more comprehensive illustrations of
+the role of precedent in American jurisprudence,
+but we figured this one would be the least likely to cause readers to think
+that NSHipster got acqui-hired by
+[Atrium](https://www.atrium.co) or something.
+
+{% endinfo %}
+
+Adherence to precedence confers inertia in the decision-making process.
+It promotes stability throughout the legal system
+and the institutions that rely on a consistent application of laws.
+
+However,
+like inertia,
+precedence can also be overcome with sufficiently compelling reasons;
+we are bound by the past only insofar as to give it due consideration.
+
+---
+
+Bearing all of that in mind,
+let's [smash cut](https://en.wikipedia.org/wiki/Smash_cut)
+to our subject for this week's brief:
+Apple Push Notification Device Tokens ---
+and in particular,
+a single change in iOS 13 that may incidentally break push notifications
+for thousands of apps.
+
+## A Push Notifications Primer
+
+Push notifications allow apps to communicate with users
+in response to remote events when they aren't currently in use.
+
+Unlike SMS or email,
+which allows a sender to communicate with a recipient directly
+using a unique identifier (a phone number and email address, respectively),
+communication between the app's remote server and the user's local device
+are facilitated by the Apple Push Notification service
+(APNs).
+
+Here's how that works:
+
+- After launching an app,
+ the app calls the method
+ [`registerForRemoteNotifications()`](https://developer.apple.com/documentation/uikit/uiapplication/1623078-registerforremotenotifications),
+ prompting the user to grant the app permission to send push notifications.
+- In response to permission being granted,
+ the app delegate calls the method
+ [`application(_:didRegisterForRemoteNotificationsWithDeviceToken:)`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622958-application).
+
+The `deviceToken` parameter in the app delegate method
+is an opaque `Data` value ---
+kind of like a really long unique phone number or email address ---
+that the app's push notification provider uses
+to route notifications through APNs to reach
+this particular installation of the app.
+
+In principle,
+representing this parameter as a `Data` value makes a lot of sense ---
+the value itself is meaningless.
+However, in practice,
+this API design decision has been the source of untold amounts of heartache.
+
+## The Enduring Challenges of Sending Device Tokens Back to the Server
+
+When the app delegate receives its `deviceToken`,
+that's not the end of the story.
+In order for its to be used to send push notifications,
+it needs to be sent from the client to the server.
+
+The question is, _"How"_?
+
+Before you jump to a particular answer,
+consider the historical context of iOS 3 (circa 2009),
+when push notifications were first introduced:
+
+### _"Back in My Day..."_
+
+You could create an [`NSURLRequest`](https://developer.apple.com/documentation/foundation/nsurlrequest) object,
+set its `httpBody` property to the `deviceToken`,
+and send it using [`NSURLConnection`](https://developer.apple.com/documentation/foundation/nsurlconnection),
+but you'd probably also want to include some additional information ---
+like a username or email address ---
+to associate it with an account in the app.
+That meant that the `data` you set as a request's HTTP body
+couldn't just be the device token.
+
+Sending an HTTP `POST` body with`application/x-www-form-urlencoded`
+(e.g. `username=jappleseed&deviceToken=____`)
+is one possibility for encoding multiple fields into a single payload,
+but then the question becomes,
+_"How do you encode binary data into text?"_
+
+[Base64](https://en.wikipedia.org/wiki/Base64)
+is a great binary-to-text encoding strategy,
+but [`NSData -base64EncodedStringWithOptions:`](https://developer.apple.com/documentation/foundation/nsdata/1413546-base64encodedstringwithoptions?language=objc)
+wouldn't be available until iOS 7,
+four years after push notifications were first introduced in iOS 3.
+Without [CocoaPods](/cocoapods/) or a strong open-source ecosystem
+to fill in the gaps,
+you were left to follow
+[blog posts](https://www.cocoawithlove.com/2009/06/base64-encoding-options-on-mac-and.html)
+describing how to roll your own implementation,
+hoping that things would work as advertised.
+
+{% info %}
+
+In retrospect,
+probably the best answer
+(at least in terms of taking advantage of built-in functionality)
+would be to serialize an `NSDictionary`
+containing the device token and other information
+into a property list.
+
+Then again,
+support for `.plist` files server-side is historically thin.
+So maybe that wouldn't have been any better, after all...
+
+{% endinfo %}
+
+Given the complexity in using Base64 encoding on iOS < 7,
+most developers instead opted to take advantage of
+what they saw as an easier, built-in alternative:
+
+### NSData, in its Own Words
+
+Developers,
+in an attempt to understand what exactly
+this `deviceToken` parameter was,
+would most likely have passed it into an `NSLog` statement:
+
+```obj-c
+NSLog(@"%@", deviceToken);
+// Prints "<965b251c 6cb1926d e3cb366f dfb16ddd e6b9086a 8a3cac9e 5f857679 376eab7C>"
+```
+
+Unfortunately,
+for developers less versed in matters of data and encoding,
+this output from `NSLog` may have led them astray:
+_"Oh wow, so `deviceToken` is actually a string!
+(I wonder why Apple was making this so difficult in the first place).
+But no matter --- I can take it from here."_
+
+```obj-c
+// ⚠️ Warning: Don't do this
+NSString *token = [[[[deviceToken description]
+ stringByReplacingOccurrencesOfString:@" " withString:@""]
+ stringByReplacingOccurrencesOfString:@"<" withString:@""]
+ stringByReplacingOccurrencesOfString:@">" withString:@""];
+```
+
+It's unclear whether push notification service providers spurred this practice
+by requiring Base16 / hexadecimal representations from the beginning,
+or if they adopted it in response to how folks were
+already accustomed to doing it,
+but either way,
+the practice stuck.
+And for nearly a decade,
+this was how a significant percentage of apps were handling
+push notification device token registration.
+
+That was until Swift 3 and iOS 10.
+
+{% info %}
+
+Granted,
+there's no single, canonical way to represent binary data as text ---
+the same token could be represented in Base64 as
+`"llslHGyxkm3jyzZv37Ft3ea5CGqKPKyeX4V2eTduq3w="`
+or in [Ascii85 encoding](https://en.wikipedia.org/wiki/Ascii85) as
+`"Q[D"`
+or in [Base🧑 encoding](https://github.com/Flight-School/Guide-to-Swift-Strings-Sample-Code#base-encoding) as
+`"👩🏻🦱👩🏻🦱👩🏼🦳👩🏻🦱👨🏻🦱👨🏻🦰👩🏾👩🏽🦳👩🏻🦰👩🏻🦲👩🏿👩🏻👩🏾👩🏾🦰👨🏿👩🏽🦱👩🏿👩🏿🦳👨🏻👩🏽👩🏿👩👨🏿🦰👩🏿🦱👨🦱👨🏻🦰👩🏼🦱👨🏼👨🏽👨🏼👩🏾👩👨🏾🦲👩🏿🦰👨🏾🦰👩🏾🦳👩👨🏽🦳👨🏿🦳👩🏽🦰👩🏼🦱👩🏿👩🏽🦲🤡"`.
+But if your push notification service provider expects device tokens
+in its classic, Base16 hexadecimal string representation,
+you should do adopt the approach described above.
+
+{% endinfo %}
+
+### Relitigating the Past with Swift 3
+
+By 2016,
+Swift had stabilized and matured to the point that
+most if not many developers were choosing to write new apps in Swift,
+or at least write all new code in Swift for existing apps.
+
+For those who did,
+the transition to Swift 3
+was most memorable for its painful migration from Swift 2.
+As part of ["the grand API renaming"](https://github.com/apple/swift-evolution/blob/master/proposals/0005-objective-c-name-translation.md)
+common Foundation types, including `NSData`,
+dropped their `NS` prefix in APIs,
+using a bridged, Swift value type in its place.
+For the most part,
+things worked as expected.
+But there were a few differences in behavior ---
+largely _undocumented or incidental behavior_
+that caused a breaking change.
+For example,
+consider the following change in
+`application(_:didRegisterForRemoteNotificationsWithDeviceToken:)`:
+
+```swift
+// Swift 2: deviceToken is NSData
+deviceToken.description // "<965b251c 6cb1926d e3cb366f dfb16ddd e6b9086a 8a3cac9e 5f857679 376eab7C>"
+
+// Swift 3: deviceToken is Data
+deviceToken.description // "32 bytes"
+```
+
+However,
+many developers remained undeterred by what was seen as a minor annoyance,
+and worked around the issue by recasting to `NSData` and its former behavior:
+
+```swift
+// ⚠️ Warning: Don't do this
+let tokenData = deviceToken as NSData
+let token = "\(tokenData)".replacingOccurrences(of: " ", with: "")
+ .replacingOccurrences(of: "<", with: "")
+ .replacingOccurrences(of: ">", with: "")
+```
+
+Once again,
+doing things the wrong way
+managed to keep things working for another couple years.
+
+But that's all coming to an end with iOS 13.
+
+{% warning %}
+
+The impact of this change is significant,
+and bears repeating:
+
+If your implementation of
+`application(_:didRegisterForRemoteNotificationsWithDeviceToken:)`
+involves converting `deviceToken` to a `String`
+and calling `replacingOccurrences(of:with:)`,
+this approach won't work in apps compiled with the iOS 13 SDK.
+This is true whether your app is written in Swift or Objective-C.
+
+{% endwarning %}
+
+### Overturned in iOS 13
+
+iOS 13 changes the format of descriptions
+for Foundation objects,
+including `NSData`:
+
+```swift
+// iOS 12
+(deviceToken as NSData).description // "<965b251c 6cb1926d e3cb366f dfb16ddd e6b9086a 8a3cac9e 5f857679 376eab7C>"
+
+// iOS 13
+(deviceToken as NSData).description // "{length = 32, bytes = 0x965b251c 6cb1926d e3cb366f dfb16ddd ... 5f857679 376eab7c }"
+```
+
+Whereas previously,
+you could coerce `NSData` to spill its entire contents
+by converting it into a `String`,
+it now reports its length and a truncated summary of its internal bytes.
+
+So from now on,
+if you need to convert your push notification registration `deviceToken`
+into a Base16-encoded / hexadecimal string representation,
+you should do the following:
+
+```swift
+let deviceTokenString = deviceToken.map { String(format: "%02x", $0) }.joined()
+```
+
+For clarity, let's break this down and explain each part:
+
+- The `map` method operates on each element of a sequence.
+ Because `Data` is a sequence of bytes in Swift,
+ the passed closure is evaluated for each byte in `deviceToken`.
+- The [`String(format:)`](https://developer.apple.com/documentation/swift/string/3126742-init) initializer
+ evaluates each byte in the data
+ (represented by the anonymous parameter `$0`)
+ using the [`%02x` format specifier](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Strings/Articles/formatSpecifiers.html),
+ to produce a zero-padded, 2-digit hexadecimal representation of
+ the byte / 8-bit integer.
+- After collecting each byte representation created by the `map` method,
+ `joined()` concatenates each element into a single string.
+
+{% info %}
+
+We'd have preferred to use the `String(_:radix:)` initializer
+to create a hexadecimal (radix = 16) string representation
+for each `UInt8` byte value,
+but without a built-in [left-pad](https://www.theregister.co.uk/2016/03/23/npm_left_pad_chaos/) function
+in the Swift standard library
+to zero-pad to two digits,
+we opted for `printf` format specifiers
+([despite our misgivings](/expressiblebystringinterpolation/)).
+
+On Stack Overflow,
+[the top-rated answer](https://stackoverflow.com/a/24979958/157142)
+advocates for `"%02.2hhx"` instead of `"%02x"`.
+It's easy to get lost in the
+[IEEE specification](http://www.opengroup.org/onlinepubs/009695399/functions/printf.html),
+so here are some minimal code examples to demonstrate the difference
+between the two format specifiers:
+
+```swift
+// Overflow UInt.max (255)
+String(format: "%02.2hhx", 256) // "00"
+String(format: "%02x", 256) // "100"
+
+// Underflow UInt.min (0)
+String(format: "%02.2hhx", -1) // "ff"
+String(format: "%02x", -1) // "ffffffff"
+```
+
+`"%02.2hhx"` guarantees that values beyond the range of `UInt`
+produce two hexadecimal digits
+(though one could argue whether it's better to fail silently here).
+
+But any difference in behavior is moot,
+so long as `Data` is a collection whose `Element` is `UInt8`:
+
+```swift
+(UInt.min...UInt.max).map {
+ String(format: "%02.2hhx", $0) == String(format: "%02x", $0)
+}.contains(false) // false
+```
+
+Oh, and don't worry about any purported performance differences
+between `reduce` and `map` + `join`;
+any Δ is going to be negligible,
+and totally irrelevant for an infrequent operation such as this.
+
+{% endinfo %}
+
+---
+
+Was Apple irresponsible in making this particular change? \\
+We'd argue: _No, not really._
+
+**Developers shouldn't have relied on a specific format for
+an object's [`description`](https://developer.apple.com/documentation/objectivec/nsobjectprotocol/1418746-description)**.
+
+Dumping an entire `Data` value's contents becomes untenable at a certain point,
+and this change to a more succinct summary
+makes debugging larger data blobs significantly easier.
+
+---
+
+Like we said about laws at the start of this article,
+precedence is a form of inertia,
+not an immutable truth.
+
+Stare decisis plays an important role
+throughout software engineering.
+Examples like the ["Referer" \[sic\] header"](https://en.wikipedia.org/wiki/HTTP_referer) ---
+even the conventions we have about
+[the direction of electricity flow](https://en.wikipedia.org/wiki/Electric_current#Conventions) ---
+demonstrate the value of sticking to decisions,
+unless an opportunity arises to compel change.
+
+[apple v. pepper]: https://www.oyez.org/cases/2018/17-204 "Apple v. Pepper, 587 U.S. ___ (2019)"
+[illinois brick]: https://www.oyez.org/cases/1976/76-404 "Illinois Brick Co. v. Illinois, 431 U.S. 720 (1977)"
+[hanover shoe]: https://www.oyez.org/cases/1967/335 "Hanover Shoe, Inc. v. United Shoe Machinery Corporation, 392 US 481 (1968)"
diff --git a/2019-09-23-ios-13.md b/2019-09-23-ios-13.md
new file mode 100644
index 00000000..d6f5a9a0
--- /dev/null
+++ b/2019-09-23-ios-13.md
@@ -0,0 +1,734 @@
+---
+title: iOS 13
+author: Mattt
+category: Miscellaneous
+excerpt: >-
+ To mark last week's release of iOS 13,
+ we're taking a look at some obscure (largely undocumented) APIs
+ that you can now use in your apps.
+status:
+ swift: 5.1
+---
+
+Apple announced a lot at WWDC this year.
+During the conference and the days that followed,
+a lot of us walked around in a daze,
+attempting to recover from have our minds _"hashtag-mindblown'd"_ (#🤯).
+But now a few months later,
+after everything announced has launched
+_([well, almost everything](https://appleinsider.com/articles/19/09/16/what-apple-is-holding-back-for-ios-131))_
+those very same keynote announcements now elicit far different reactions:
+
+
+
+Dark Mode?
+
+😎 [Already covered it](/dark-mode/).
+
+SwiftUI?
+
+🙄 [Give it another year or two](/wwdc-2019/).
+
+Augmented Reality?
+
+🥱 [Wake us up when Apple announces AR glasses.](http://appleinsider.com/articles/17/01/09/rumor-apple-working-with-carl-zeiss-on-ar-glasses-to-debut-in-2018)
+
+
+
+
+
+Although the lion's share of attention
+has been showered on the aforementioned features,
+not nearly enough coverage has been given to the rest of iOS 13 ---
+and that's a shame,
+because this release is among the most exciting in terms of new functionality
+and satisfying in terms of improving existing functionality.
+
+So to mark last week's release of iOS 13,
+we're taking a look at some obscure (largely undocumented) APIs
+that you can now use in your apps.
+We've scavenged the best bits out of the
+[iOS 13 Release Notes](https://developer.apple.com/documentation/ios_ipados_release_notes/ios_13_release_notes)
+[API diffs](http://codeworkshop.net/objc-diff/sdkdiffs/ios/13.0/),
+and now present them to you.
+
+Here are some of our favorite things you can do starting in iOS 13:
+
+---
+
+## Generate Rich Representations of URLs
+
+New in iOS 13,
+the
+[LinkPresentation framework](https://developer.apple.com/documentation/LinkPresentation)
+provides a convenient, built-in way to replicate the
+rich previews of URLs you see in Messages.
+If your app has any kind of chat or messaging functionality,
+you'll definitely want to check this out.
+
+Rich previews of URLs have a rich history
+going at least as far back as the early '00s,
+with the spread of
+[Microformats](http://microformats.org)
+by semantic web pioneers,
+and early precursors to Digg and Reddit
+using [khtml2png](https://github.com/xsuchy/khtml2png)
+to generate thumbnail images of web pages.
+Fast forward to 2010,
+with the rise of social media and user-generated content,
+when Facebook created the [OpenGraph protocol](https://ogp.me)
+to allow web publishers to customize how their pages looked
+when posted on the Newsfeed.
+
+These days,
+most websites reliably have OpenGraph `` tags on their site
+that provide a summary of their content for
+social networks, search engines, and anywhere else that links are trafficked.
+For example,
+here's what you would see if you did "View Source" for this very webpage:
+
+{% assign description = page.excerpt | strip_html | strip %}
+
+```html
+
+
+
+
+
+
+```
+
+If you wanted to consume this information in your app,
+you can now use the LinkPresentation framework's `LPMetadataProvider` class
+to fetch the metadata and optionally construct a representation:
+
+```swift
+import LinkPresentation
+
+let metadataProvider = LPMetadataProvider()
+let url = URL(string: "{{ page.url | absolute_url }}")!
+
+metadataProvider.startFetchingMetadata(for: url) { [weak self] metadata, error in
+ guard let metadata = metadata else { return }
+
+ let linkView = LPLinkView(metadata: metadata)
+ self?.view.addSubview(linkView)
+}
+```
+
+After setting appropriate constraints
+(and perhaps a call to `sizeToFit()`),
+you'll get the following,
+which the user can tap to preview the linked webpage:
+
+
+
+
+
+
+{% info %}
+
+In the `startFetchingMetadata(for:)` completion handler,
+you can inspect and mutate the metadata retrieved from the server.
+You might take this opportunity to add default images / videos
+for pages that don't have them,
+translate text into one of the user's preferred languages,
+or censor explicit text and media.
+
+{% endinfo %}
+
+Alternatively,
+if you already have the metadata in-app,
+or otherwise can't or don't want to fetch remotely,
+you can construct an `LPLinkMetadata` directly:
+
+```swift
+let metadata = LPLinkMetadata()
+metadata.url = url
+metadata.title = "{{ page.title }}"
+metadata.iconProvider = <#...#>
+
+let linkView = LPLinkView(metadata: metadata)
+```
+
+{% warning %}
+
+`LPMetadataProvider` works as advertised on iOS,
+but macOS clients must have the `com.apple.security.network.client` entitlement
+in order to fetch metadata from remote URLs.
+
+{% endwarning %}
+
+## Perform On-Device Speech Recognition
+
+[`SFSpeechRecognizer`](https://developer.apple.com/documentation/speech/sfspeechrecognizer)
+gets a major upgrade in iOS 13 —
+most notably for its added support for on-device speech recognition.
+
+Previously,
+transcription required an internet connection
+and was restricted to a maximum of 1-minute duration
+with daily limits for requests.
+But now,
+you can do speech recognition completely on-device and offline,
+with no limitations.
+The only caveats are that
+offline transcription isn't as good as what you'd get with a server connection,
+and is only available for certain languages.
+
+To determine whether offline transcription is available for the user's locale,
+check the `SFSpeechRecognizer` property
+[`supportsOnDeviceRecognition`](https://developer.apple.com/documentation/speech/sfspeechrecognizer/3152604-supportsondevicerecognition).
+At the time of publication, the list of supported languages are as follows:
+
+
+
+English
+: United States (`en-US`)
+: Canada (`en-CA`)
+: Great Britain (`en-GB`)
+: India (`en-IN`)
+
+Spanish
+: United States (`es-US`)
+: Mexico (`es-MX`)
+: Spain (`es-ES`)
+
+Italian
+: Italy (`it-IT`)
+
+Portuguese
+: Brazil (`pt-BR`)
+
+Russian
+: Russia (`ru-RU`)
+
+Turkish
+: Turkey (`tr-TR`)
+
+Chinese
+: Mandarin (`zh-cmn`)
+: Cantonese (`zh-yue`)
+
+
+
+{% warning %}
+
+According to the iOS 13 release notes:
+
+> The `supportsOnDeviceRecognition` property always returns `false`
+> the first time it’s accessed.
+> After a few seconds,
+> accessing it again returns the correct value.
+
+This is now resolved in iOS 13.2:
+
+> The supportsOnDeviceRecognition property now returns the correct value. (47822242)
+
+{% endwarning %}
+
+But that's not all for speech recognition in iOS 13!
+`SFSpeechRecognizer` now provides information including
+speaking rate and average pause duration,
+as well as voice analytics features like
+jitter (variations in pitch) and
+shimmer (variations in amplitude).
+
+```swift
+import Speech
+
+guard SFSpeechRecognizer.authorizationStatus() == .authorized,
+ let recognizer = SFSpeechRecognizer()
+else {
+ fatalError()
+}
+
+let url: URL = <#...#>
+let request = SFSpeechURLRecognitionRequest(url: url)
+
+recognizer.recognitionTask(with: request) { (result, error) in
+ guard let result = result else { return }
+
+ for segment in result.bestTranscription.segments {
+ guard let voiceAnalytics = segment.voiceAnalytics else { continue }
+
+ let pitch = voiceAnalytics.pitch.acousticFeatureValuePerFrame
+ let voicing = voiceAnalytics.voicing.acousticFeatureValuePerFrame
+ let jitter = voiceAnalytics.jitter.acousticFeatureValuePerFrame
+ let shimmer = voiceAnalytics.shimmer.acousticFeatureValuePerFrame
+ }
+}
+```
+
+Information about pitch and voicing and other features
+could be used by your app
+(perhaps [in coordination with CoreML](https://developer.apple.com/documentation/createml/mlsoundclassifier))
+to differentiate between speakers
+or determine subtext from a speaker's inflection.
+
+## Send and Receive Web Socket Messages
+
+Speaking of the Foundation URL Loading System,
+we now have native support for
+something that's been at the top of our wish list for many years:
+web sockets.
+
+Thanks to the new
+[`URLSessionWebSocketTask`](https://developer.apple.com/documentation/foundation/urlsessionwebsockettask) class
+in iOS 13,
+you can now incorporate real-time communications in your app
+as easily and reliably as sending HTTP requests ---
+all without any third-party library or framework:
+
+```swift
+let url = URL(string: "wss://<#...#>")!
+let webSocketTask = URLSession.shared.webSocketTask(with: url)
+webSocketTask.resume()
+
+// Send one message
+let message: URLSessionWebSocketTask.Message = .string("Hello, world!")
+webSocketTask.send(message) { error in
+ <#...#>
+}
+
+// Receive one message
+webSocketTask.receive { result in
+ guard case let .success(message) = result else { return }
+ <#...#>
+}
+
+// Eventually...
+webSocketTask.cancel(with: .goingAway, reason: nil)
+```
+
+{% info %}
+
+For lower-level control over web sockets,
+including both client and server support,
+check out the
+[Network framework](https://developer.apple.com/documentation/network).
+
+{% endinfo %}
+
+For years now,
+networking has been probably the fastest-moving part
+of the whole Apple technology stack.
+Each WWDC,
+there's so much to talk about that
+that they routinely have to break their content across two separate sessions.
+2019 was no exception,
+and we highly recommend that you take some time to check out this year's
+"Advances in Networking" sessions
+([Part 1](https://developer.apple.com/videos/play/wwdc2019/712/),
+[Part 2](https://developer.apple.com/videos/play/wwdc2019/713)).
+
+## Do More With Maps
+
+MapKit is another part of Apple SDKs
+that's consistently had a strong showing at WWDC year after year.
+And it's often the small touches that make the most impact in our day-to-day.
+
+For instance,
+the new
+[`MKMapView.CameraBoundary`](https://developer.apple.com/documentation/mapkit/mkmapview/cameraboundary) API
+in iOS 13
+makes it much easier to constrain a map's viewport to a particular region
+without locking it down completely.
+
+```swift
+let region = MKCoordinateRegion(center: mapView.center,
+ latitudinalMeters: 1000,
+ longitudinalMeters: 1000)
+mapView.cameraBoundary = MKMapView.CameraBoundary(coordinateRegion: region)
+```
+
+And with the new
+[`MKPointOfInterestFilter`](https://developer.apple.com/documentation/mapkit/mkpointofinterestfilter) API,
+you can now customize the appearance of map views
+to show only certain kinds of points of interest
+_(whereas previously it was an [all-or-nothing](https://developer.apple.com/documentation/mapkit/mkmapview/1452102-showspointsofinterest) proposition)_.
+
+```swift
+let filter = MKPointOfInterestFilter(including: [.cafe])
+mapView.pointOfInterestFilter = filter // only show cafés
+```
+
+Finally,
+with [`MKGeoJSONDecoder`](https://developer.apple.com/documentation/mapkit/mkgeojsondecoder),
+we now have a built-in way to pull in [GeoJSON](https://geojson.org) shapes
+from web services and other data sources.
+
+```swift
+let decoder = MKGeoJSONDecoder()
+
+if let url = URL(string: "<#...#>"),
+ let data = try? Data(contentsOfURL: url),
+ let geoJSONObjects = try? decoder.decode(data) {
+
+ for case let overlay as MKOverlay in geoJSONObjects {
+ mapView.addOverlay(overlay)
+ }
+}
+```
+
+## Keep Promises in JavaScript
+
+If you enjoyed our article about [JavaScriptCore](/javascriptcore/),
+you'd be thrilled to know that `JSValue` objects
+[now natively support promises](https://developer.apple.com/documentation/javascriptcore/jsvalue/3335012-init).
+
+For the uninitiated:
+in JavaScript, a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
+is an object that represents the eventual completion (or rejection)
+of an asynchronous operation
+and its resulting value.
+Promises are a mainstay of modern JS development ---
+perhaps most notably within the [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) API.
+
+Another addition to JavaScriptCore in iOS 13
+is support for [symbols](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol)
+(no, not [those symbols](https://developer.apple.com/design/human-interface-guidelines/sf-symbols/)).
+For more information about
+[`init(newSymbolFromDescription:in:)`](https://developer.apple.com/documentation/javascriptcore/jsvalue/3042804-init),
+refer to the docs
+just guess how to use it.
+
+## Respond to Objective-C Associated Objects (?)
+
+On a lark,
+we decided to see if there was anything new in Objective-C this year
+and were surprised to find out about
+[objc_setHook_setAssociatedObject](https://developer.apple.com/documentation/objectivec/objc_hook_setassociatedobject?language=objc).
+Again, we don't have much to go on except the declaration,
+but it looks like you can now configure a block to execute when
+an [associated object](/associated-objects/) is set.
+For anyone still deep in the guts of the Objective-C runtime,
+this sounds like it could be handy.
+
+## Tame Activity Items (?)
+
+On the subject of missing docs:
+[`UIActivityItemsConfiguration`](https://developer.apple.com/documentation/uikit/uiactivityitemsconfiguration)
+_seems_ like a compelling option for managing
+actions in the new iOS 13 share sheets,
+but we don't really know where to start...
+
+
+
+Shame that we don't have the information we need to take advantage of this yet.
+
+## Format Lists and Relative Times
+
+As discussed in [a previous article](/formatter/),
+iOS 13 brings two new formatters to Foundation:
+[`ListFormatter`](https://developer.apple.com/documentation/foundation/listformatter)
+and
+[`RelativeDateTimeFormatter`](https://developer.apple.com/documentation/foundation/relativedatetimeformatter).
+
+Not to harp on about this,
+but both of them are _still_ undocumented,
+so if you want to learn more,
+we'd recommend checking out that article from July.
+Or, if you're in a rush
+here's a quick example demonstrating how to use both of them together:
+
+```swift
+import Foundation
+
+let relativeDateTimeFormatter = RelativeDateTimeFormatter()
+relativeDateTimeFormatter.dateTimeStyle = .named
+
+let listFormatter = ListFormatter()
+listFormatter.string(from: [
+ relativeDateTimeFormatter.localizedString(from: DateComponents(day: -1)),
+ relativeDateTimeFormatter.localizedString(from: DateComponents(day: 0)),
+ relativeDateTimeFormatter.localizedString(from: DateComponents(day: 1))
+]) // "yesterday, today, and tomorrow"
+```
+
+## Track the Progress of Enqueued Operations
+
+Starting in iOS 13,
+`OperationQueue` now has a
+[`progress`](https://developer.apple.com/documentation/foundation/operationqueue/3172535-progress) property.
+
+Granted,
+[`(NS)Progress`](https://developer.apple.com/documentation/foundation/progress)
+objects aren't the most straightforward or convenient things to work with
+(we've been meaning to write an article about them at some point),
+but they have a complete and well-considered API,
+and even have some convenient slots in app frameworks.
+
+For example,
+check out how easy it is to wire up a `UIProgressView`
+to display the live-updating progress of an operation queue
+by way of its [`observedProgress` property](https://developer.apple.com/documentation/uikit/uiprogressview/1619840-observedprogress):
+
+```swift
+import UIKit
+
+fileprivate class DownloadOperation: Operation { <#...#> }
+
+class ViewController: UIViewController {
+ private let operationQueue = {
+ let queue = OperationQueue()
+ queue.maxConcurrentOperationCount = 1
+ }()
+
+ @IBOutlet private var progressView: UIProgressView!
+
+ @IBAction private func startDownloading(_ sender: Any) {
+ operationQueue.cancelAllOperations()
+ progressView.observedProgress = operationQueue.progress
+
+ for url in [<#...#>] {
+ let operation = DownloadOperation(url: url)
+ operationQueue.addOperation(operation)
+ }
+ }
+}
+```
+
+It's also worth mentioning a few other APIs coming to in 13,
+like
+[`schedule(after:interval:tolerance:options:_:)`](https://developer.apple.com/documentation/foundation/operationqueue/3329364-schedule),
+which clues `OperationQueue` into the new
+[Combine framework](https://developer.apple.com/documentation/combine)
+in a nice way,
+and [`addBarrierBlock(_:)`](https://developer.apple.com/documentation/foundation/operationqueue/3172534-addbarrierblock),
+which presumably works like
+[Dispatch barrier blocks](https://developer.apple.com/documentation/dispatch/1452917-dispatch_barrier_sync?language=objc)
+(though without documentation, it's anyone's guess).
+
+## Manage Background Tasks with Ease
+
+One of the things that often differentiates category-defining apps from their competitors
+is their use of background tasks
+to make sure the app is fully synced and updated for the next time
+it enters the foreground.
+
+iOS 7 was the first release to provide
+[an official API for scheduling background tasks](https://developer.apple.com/documentation/uikit/uiapplication/1623051-beginbackgroundtask)
+_(though developers had employed various creative approaches prior to this)_.
+But in the intervening years,
+multiple factors ---
+from an increase in iOS app capabilities and complexity
+to growing emphasis in performance, efficiency, and privacy for apps ---
+have created a need for a more comprehensive solution.
+
+That solution came to iOS 13 by way of the new
+[BackgroundTasks framework](https://developer.apple.com/documentation/backgroundtasks?language=objc).
+
+As described in this year's WWDC session
+["Advances in App Background Execution"](https://developer.apple.com/videos/play/wwdc2019/707),
+the framework distinguishes between two broad classes of background tasks:
+
+- app refresh tasks:
+ short-lived tasks that keep an app up-to-date throughout the day
+- background processing tasks:
+ long-lived tasks for performing deferrable maintenance tasks
+
+The WWDC session and the accompanying sample code project
+do a great job of explaining how to incorporate both of these
+into your app.
+But if you want the quick gist of it,
+here's a small example of an app that schedules periodic refreshes
+from a web server:
+
+```swift
+import UIKit
+import BackgroundTasks
+
+fileprivate let backgroundTaskIdentifier = "com.nshipster.example.task.refresh"
+
+@UIApplicationMain
+class AppDelegate: UIResponder, UIApplicationDelegate {
+ var window: UIWindow?
+
+ lazy var backgroundURLSession = {
+ let configuration = URLSessionConfiguration.background(withIdentifier: "com.nshipster.url-session.background")
+ configuration.discretionary = true
+ configuration.timeoutIntervalForRequest = 30
+
+ return URLSession(configuration: configuration, delegate: <#...#>, delegateQueue: <#...#>)
+ }
+
+ func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
+ <#...#>
+
+ BGTaskScheduler.shared.register(forTaskWithIdentifier: backgroundTaskIdentifier, using: nil) { task in
+ self.handleAppRefresh(task: task as! BGAppRefreshTask)
+ }
+
+ return true
+ }
+
+ func applicationDidEnterBackground(_ application: UIApplication) {
+ scheduleAppRefresh()
+ }
+
+ func scheduleAppRefresh() {
+ let request = BGAppRefreshTaskRequest(identifier: backgroundTaskIdentifier)
+ request.earliestBeginDate = Date(timeIntervalSinceNow: 60 * 10)
+
+ do {
+ try BGTaskScheduler.shared.submit(request)
+ } catch {
+ print("Couldn't schedule app refresh: \(error)")
+ }
+ }
+
+ func handleAppRefresh(task: BGAppRefreshTask) {
+ scheduleAppRefresh()
+
+ let url: URL = <#...#>
+ var dataTask = backgroundURLSession.dataTask(with: url) { (data, response, error) in
+ <#...#>
+ let success = (200..<300).contains(response?.statusCode)
+ task.setTaskCompleted(success: success)
+ }
+
+ task.expirationHandler = {
+ dataTask.cancel()
+ }
+
+ dataTask.resume()
+ }
+
+ <#...#>
+}
+```
+
+{% info %}
+
+The previous way of doing background updates ---
+namely,
+`UIApplication.setMinimumBackgroundFetchInterval(_:)` and
+`UIApplicationDelegate.application(_:performFetchWithCompletionHandler:)` ---
+are now deprecated in iOS 13.
+
+{% endinfo %}
+
+## Annotate Text Content Types for Better Accessibility
+
+You know how frustrating it is to hear some people read out URLs?
+("eɪʧ ti ti pi ˈkoʊlən slæʃ slæʃ ˈdʌbəlju ˈdʌbəlju ˈdʌbəlju dɑt"...)
+That's what it can be like when
+[VoiceOver](https://www.apple.com/accessibility/iphone/vision/)
+tries to read something without knowing more about _what_ it's reading.
+
+iOS 13 promises to improve the situation considerably
+with the new [`accessibilityTextualContext`](https://developer.apple.com/documentation/objectivec/nsobject/3075456-accessibilitytextualcontext) property
+and [`UIAccessibilityTextAttributeContext`](https://developer.apple.com/documentation/uikit/uiaccessibilitytextattributecontext) `NSAttributedString` attribute key.
+Whenever possible,
+be sure to annotate views and attributed strings with
+the constant that best describes the kind of text being displayed:
+
+- `UIAccessibilityTextualContextConsole`
+- `UIAccessibilityTextualContextFileSystem`
+- `UIAccessibilityTextualContextMessaging`
+- `UIAccessibilityTextualContextNarrative`
+- `UIAccessibilityTextualContextSourceCode`
+- `UIAccessibilityTextualContextSpreadsheet`
+- `UIAccessibilityTextualContextWordProcessing`
+
+{% info %}
+
+For more information,
+see the WWDC 2019 session
+["Creating an Accessible Reading Experience"](https://developer.apple.com/videos/play/wwdc2019/248/).
+
+{% endinfo %}
+
+## Remove Implicitly Unwrapped Optionals from View Controllers Initialized from Storyboards
+
+SwiftUI may have signaled the eventual end of Storyboards,
+but that doesn't mean that things aren't and won't continue to get better
+until if / when that day comes.
+
+One of the most irritating anti-patterns for Swift purists
+when working on iOS projects with Storyboards
+has been view controller initialization.
+Due to an impedance mismatch between
+Interface Builder's "prepare for segues" approach and
+Swift's object initialization rules,
+we frequently had to choose between
+making all of our properties non-private, variable, and (implicitly unwrapped) optionals,
+or foregoing Storyboards entirely.
+
+Xcode 11 and iOS 13 allow these paradigms to reconcile their differences
+by way of the new `@IBSegueAction` attribute
+and some new `UIStoryboard` class methods:
+
+First,
+the `@IBSegueAction` attribute
+can be applied view controller method declarations
+to designate itself as the API responsible for
+creating a segue's destination view controller
+_(i.e. the `destinationViewController` property of the `segue` parameter
+in the `prepare(for:sender:)` method)_.
+
+```swift
+@IBSegueAction
+func makeProfileViewController(coder: NSCoder, sender: Any?, segueIdentifier: String?) -> ProfileViewController? {
+ ProfileViewController(
+ coder: coder,
+ name: self.selectedName,
+ avatarImageURL: self.selectedAvatarImageURL
+ )
+}
+```
+
+Second,
+the `UIStoryboard` class methods
+[`instantiateInitialViewController(creator:)`](https://developer.apple.com/documentation/uikit/uistoryboard/3213988-instantiateinitialviewcontroller)
+and [`instantiateViewController(identifier:creator:)`](https://developer.apple.com/documentation/uikit/uistoryboard/3213989-instantiateviewcontroller`)
+offer a convenient block-based customization point for
+instantiating a Storyboard's view controllers.
+
+```swift
+import UIKit
+
+struct Person { <#...#> }
+
+class ProfileViewController: UIViewController {
+ let name: String
+ let avatarImageURL: URL?
+
+ init?(coder: NSCoder, name: String, avatarImageURL: URL?) {
+ self.name = name
+ self.avatarImageURL = avatarImageURL
+
+ super.init(coder: coder)
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+}
+
+let storyboard = UIStoryboard(name: "ProfileViewController", bundle: nil)
+storyboard.instantiateInitialViewController(creator: { decoder in
+ ProfileViewController(
+ coder: decoder,
+ name: "Johnny Appleseed",
+ avatarImageURL: nil
+ )
+})
+```
+
+Together with the new [UIKit Scene](https://developer.apple.com/documentation/uikit/app_and_environment/scenes) APIs,
+iOS 13 gives us a lot to work with
+as we wait for SwiftUI to mature and stabilize.
+
+{% asset articles/ios-13.css %}
+
+---
+
+That does it for our round-up of iOS 13 features
+that you may have missed.
+But rest assured ---
+we're planning to cover many more new APIs in future NSHipster articles.
+
+If there's anything we missed that you'd like for us to cover,
+please [@ us on Twitter](https://twitter.com/nshipster)!
diff --git a/2019-10-14-swiftui-previews.md b/2019-10-14-swiftui-previews.md
new file mode 100644
index 00000000..540d5a08
--- /dev/null
+++ b/2019-10-14-swiftui-previews.md
@@ -0,0 +1,456 @@
+---
+title: SwiftUI Previews on macOS Catalina and Xcode 11
+author: Mattt
+category: Xcode
+excerpt: >-
+ Working on a large iOS codebase often involves a lot of waiting.
+ But with Xcode 11,
+ our wait is finally over —
+ and it's all thanks to SwiftUI.
+status:
+ swift: 5.1
+---
+
+Working on a large iOS codebase often involves a lot of waiting:
+Waiting for Xcode to index your files,
+waiting for Swift and Objective-C code to compile,
+waiting for the Simulator to boot and your app to launch...
+
+And after all of that,
+you spend even more time getting your app
+into a particular state and onto a particular screen,
+just to see whether the Auto Layout constraint you just added
+fixes that regression you found.
+It didn't, of course,
+so you jump back into Xcode,
+tweak the Content Hugging Priority,
+hit ⌘R,
+and start the whole process again.
+
+We might relate our sorry predicament to
+[that one xkcd comic](https://xkcd.com/303/),
+but for those of us who don't so much relish in
+the stop-and-go nature of app development,
+there's an old Yiddish joke about Shlemiel the painter
+_(provided below with a few -specific modifications;
+for the uninitiated,
+please refer to Joel Spolsky's
+[original telling](https://www.joelonsoftware.com/2001/12/11/back-to-basics/))_:
+
+> Shlemiel gets a job as a software developer,
+> implementing a new iOS app.
+> On the first sprint he opens Xcode
+> and implements 10 new screens of the app.
+> _"That’s pretty good!"_ says his manager,
+> _"you’re a fast worker!"_ and pays him a Bitcoin.
+>
+> The next sprint Shlemiel only gets 5 screens done.
+> _"Well, that’s not nearly as good as yesterday,
+> but you’re still a fast worker. 5 screens is respectable,"_
+> and pays him a Bitcoin.
+>
+> The next sprint Shlemiel implements 1 screen.
+> _"Only 1!"_ shouts his manager.
+> _"That’s unacceptable!
+> On the first day you did ten times that much work!
+> What’s going on?"_
+>
+> _"I can’t help it,"_ says Shlemiel.
+> "Each sprint I get further and further away from
+> `application(_:didFinishLaunchingWithOptions:)`!"
+
+Over the years,
+there have been some developments that've helped things slightly,
+including
+[`@IBInspectable` and `@IBDesignable`](/ibinspectable-ibdesignable/)
+and [Xcode Playgrounds](/xcplayground/).
+But with Xcode 11,
+our wait is finally over ---
+and it's all thanks to SwiftUI.
+
+---
+
+{% warning %}
+
+The functionality described in this article requires the following:
+
+- **Xcode 11**
+- **macOS Catalina**
+- **iOS 13** set as the **Deployment Target** for your app's **Debug** configuration \\
+ _(In Xcode, navigate your project's Build Settings;
+ under the Deployment heading,
+ expand the iOS Deployment Target setting and set Debug to iOS 13.0 or later)_
+
+Without these three things,
+your code either won't compile or won't render live previews.
+
+{% endwarning %}
+
+---
+
+Although many of us have taken a [_"wait and see"_ approach](/wwdc-2019/) to SwiftUI,
+we can start using its capabilities **today**
+to radically speed up and improve our development process ---
+_without changing a line of code in our UIKit apps_.
+
+Consider a subclass of `UIButton`
+that draws a border around itself:
+
+```swift
+final class BorderedButton: UIButton {
+ var cornerRadius: CGFloat { <#...#> }
+ var borderWidth: CGFloat { <#...#> }
+ var borderColor: UIColor? { <#...#> }
+}
+```
+
+Normally,
+if we wanted to test how our UI element performs,
+we'd have to add it to a view in our app,
+build and run,
+and navigate to that screen.
+But with Xcode 11,
+we can now see a preview side-by-side with the code editor
+by adding the following under the original declaration of `BorderedButton`:
+
+
+
+Using a new feature called dynamic replacement,
+Xcode can update this preview without recompiling ---
+within moments of your making a code change.
+This lets you rapidly prototype changes like never before.
+
+Want to see how your button handles long titles?
+Bang away on your keyboard within the call to `setTitle(_:for:)`
+in your preview,
+and test out potential fixes in your underlying implementation
+without so much as leaving your current file!
+
+{% info %}
+
+`UIViewPreview` is a custom, generic structure
+that we created to conveniently host previews of `UIView` subclasses.
+Feel free to [download the source](https://gist.github.com/mattt/ff6b58af8576c798485b449269d43607)
+and add it to your project directly.
+
+Incorporating a proper dependency would be complicated by
+the conditional import and iOS 13 Deployment Target settings
+required to make Xcode Previews work for non-SwiftUI apps,
+so in this particular instance,
+we think it's best to embed these files directly.
+
+{::nomarkdown}
+
+Expand for the full implementation of UIViewPreview:
+{:/}
+
+```swift
+import UIKit
+
+#if canImport(SwiftUI) && DEBUG
+import SwiftUI
+struct UIViewPreview: UIViewRepresentable {
+ let view: View
+
+ init(_ builder: @escaping () -> View) {
+ view = builder()
+ }
+
+ // MARK: - UIViewRepresentable
+
+ func makeUIView(context: Context) -> UIView {
+ return view
+ }
+
+ func updateUIView(_ view: UIView, context: Context) {
+ view.setContentHuggingPriority(.defaultHigh, for: .horizontal)
+ view.setContentHuggingPriority(.defaultHigh, for: .vertical)
+ }
+}
+#endif
+```
+
+{::nomarkdown}
+
+{:/}
+
+{% endinfo %}
+
+## Previewing Multiple States
+
+Let's say our app had a `FavoriteButton` ---
+a distant cousin (perhaps by composition) to `BorderedButton`.
+In its default state,
+it shows has the title "Favorite"
+and displays a ♡ icon.
+When its `isFavorited` property is set to `true`,
+the title is set to "Unfavorite"
+and displays a ♡̸ icon.
+
+We can preview both at once
+by wrapping two `UIViewPreview` instances within a single SwiftUI `Group`:
+
+
+
+{% info %}
+
+The chained `previewLayout` and `padding` methods
+apply to each member of the `Group`.
+You can use these and
+[other `View` methods](https://developer.apple.com/documentation/swiftui/view)
+to change the appearance of your previews.
+
+{% endinfo %}
+
+## Previewing Dark Mode
+
+With [Dark Mode in iOS 13](/dark-mode/),
+it's always a good idea to double-check that your custom views
+are configured with dynamic colors
+or accommodate both light and dark appearance in some other way.
+
+An easy way to do this
+would be to use a `ForEach` element
+to render a preview for each case in the `ColorScheme` enumeration:
+
+
+
+{% info %}
+
+When rendering previews with `ForEach`,
+use the `previewDisplayName` method to help distinguish among
+all of the enumerated values.
+
+{% endinfo %}
+
+## Previewing Dynamic Type Size Categories
+
+We can use the same approach to preview our views in various
+[Dynamic Type Sizes](https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/typography/):
+
+
+
+## Previewing Different Locales
+
+Xcode Previews are especially time-saving when it comes to
+localizing an app into multiple languages.
+Compared to the hassle of configuring Simulator
+back and forth between different languages and regions,
+this new approach makes a world of difference.
+
+Let's say that, in addition to English,
+your app supported various [right-to-left languages](https://en.wikipedia.org/wiki/Right-to-left).
+You could verify that your
+RTL logic worked as expected like so:
+
+
+
+{% info %}
+
+We don't know of an easy way to use `NSLocalizedString` with an explicit locale.
+You could go to the trouble of retrieving localized strings
+from a strings file in your bundle,
+but in most cases,
+you'll be just fine hard-coding text in your previews.
+
+{% endinfo %}
+
+## Previewing View Controllers on Different Devices
+
+SwiftUI previews aren't limited to views,
+you can also use them with view controllers.
+By creating a [custom `UIViewControllerPreview` type](https://gist.github.com/mattt/ff6b58af8576c798485b449269d43607)
+and taking advantage of some
+[new `UIStoryboard` class methods in iOS 13](https://nshipster.com/ios-13/#remove-implicitly-unwrapped-optionals-from-view-controllers-initialized-from-storyboards),
+we can easily preview our view controller
+on various devices ---
+one on top of another:
+
+
+
+{% error %}
+
+There's currently no way to get SwiftUI device previews in landscape orientation.
+Although you can approximate this with a fixed size preview layout,
+be aware that it won't respect Safe Area on iPhone
+or render split views correctly on iPad.
+
+{% enderror %}
+
+---
+
+Although most of us are still some years away from shipping SwiftUI in our apps
+(whether by choice or necessity),
+we can all immediately benefit from the order-of-magnitude improvement
+it enables with Xcode 11 on macOS Catalina.
+
+By eliminating so much time spent waiting for things to happen,
+we not only get (literally) _hours_ more time each week,
+but we unlock the possibility of maintaining an unbroken flow state during that time.
+Not only that,
+but the convenience of integrated tests
+fundamentally changes the calculus for testing:
+instead of being a rare _"nice to have,"_
+they're the new default.
+Plus:
+these inline previews serve as living documentation
+that can help teams both large and small
+finally get a handle on their design system.
+
+It's hard to overstate how much of a game-changer Xcode Previews are for iOS development,
+and we couldn't be happier to incorporate them into our workflow.
+
+{% asset articles/swiftui-previews.css %}
diff --git a/2019-10-21-metrickit.md b/2019-10-21-metrickit.md
new file mode 100644
index 00000000..33e4b256
--- /dev/null
+++ b/2019-10-21-metrickit.md
@@ -0,0 +1,671 @@
+---
+title: MetricKit
+author: Mattt
+category: Xcode
+excerpt: >-
+ At WWDC this year,
+ Apple announced a coordinated effort between Xcode 11 and iOS 13
+ to bring new insights to developers
+ about how their apps are performing in the field.
+status:
+ swift: 5.1
+---
+
+As an undergraduate student,
+I had a radio show called
+_"Goodbye, Blue Monday"_
+(I was really into Vonnegut at the time).
+It was nothing glamorous ---
+just a weekly, 2-hour slot at the end of the night
+before the station switched into automation.
+
+If you happened to be driving through the hills of Pittsburgh, Pennsylvania
+late at night with your radio tuned to
+[WRCT 88.3](http://www.wrct.org),
+you'd have heard an eclectic mix of
+[Contemporary Classical](https://beta.music.apple.com/us/album/acoustica/410402556),
+[Acid Jazz](https://beta.music.apple.com/us/album/a-funk-odyssey/203132910),
+[Italian Disco](https://beta.music.apple.com/us/album/ma-quale-idea-single/1415038751), and
+[Bebop](https://beta.music.apple.com/us/album/kind-of-blue/268443092).
+That, and the stilting, dulcet baritone of
+a college kid doing his best impersonation of
+[Tony Mowod](http://old.post-gazette.com/magazine/20010404mowod4.asp).
+
+Sitting there in the booth,
+waiting for tracks to play out before launching into an
+FCC-mandated
+PSA
+or on-the-hour
+[station identification](https://en.wikipedia.org/wiki/Station_identification),
+I'd wonder:
+_Is anyone out there listening?_
+_And if they were, did they like it?_
+I could've been broadcasting static the whole time and been none the wiser.
+
+The same thoughts come to mind whenever I submit a build to App Store Connect...
+but then I'll remember that, unlike radio,
+you _can_ actually know these things!
+And the latest improvements in Xcode 11 make it easier than ever
+to get an idea of how your apps are performing in the field.
+
+We'll cover everything you need to know in this week's NSHipster article.
+So as they say on the radio:
+_"Don't touch that dial (it's got jam on it)"._
+
+
+
+MetricKit is a new framework in iOS 13
+for collecting and processing battery and performance metrics.
+It was announced at [WWDC this year](/wwdc-2019/)
+along with XCTest Metrics and the Xcode Metrics Organizer
+as part of a coordinated effort to bring new insights to developers
+about how their apps are performing in the field.
+
+{::nomarkdown}
+
+
+
+
+
+
+
+
+Diagram from WWDC 2019 Session 417: "Improving Battery Life and Performance"
+
+
+{:/}
+
+Apple automatically collects metrics from apps installed on the App Store.
+You can view them in Xcode 11
+by opening the Organizer (⌥⌘⇧O)
+and selecting the new Metrics tab.
+
+MetricKit complement Xcode Organizer Metrics by providing a programmatic way to
+receive daily information about how your app is performing in the field.
+With this information,
+you can collect, aggregate, and analyze on your own in greater detail
+than you can through Xcode.
+
+## Understanding App Metrics
+
+Metrics can help uncover issues you might not have seen while testing locally,
+and allow you to track changes across different versions of your app.
+For this initial release,
+Apple has focused on the two metrics that matter most to users:
+battery usage and performance.
+
+### Battery Usage
+
+
+
+
+
+
+Battery life depends on a lot of different factors.
+Physical aspects like the age of the device and
+the number of charge cycles are determinative,
+but the way your phone is used matters, too.
+Things like CPU usage,
+the brightness of the display and the colors on the screen,
+and how often radios are used to fetch data or get your current location ---
+all of these can have a big impact.
+But the main thing to keep in mind is that
+users care a lot about battery life.
+
+Aside from how good the camera is,
+the amount of time between charges
+is _the_ deciding factor when someone buys a new phone these days.
+So when their new, expensive phone _doesn't_ make it through the day,
+they're going to be pretty unhappy.
+
+Until recently,
+Apple's taken most of the heat on battery issues.
+But since iOS 12 and its new
+[Battery Usage screen](https://support.apple.com/en-us/HT201264) in Settings,
+users now have a way to tell when their favorite app is to blame.
+Fortunately,
+with iOS 13 you now have everything you need to make sure
+your app doesn't run afoul of reasonable energy usage.
+
+### Performance
+
+Performance is another key factor in the overall user experience.
+Normally, we might look to stats like
+processor clock speed or [frame rate](/uitableviewheaderfooterview/)
+as a measure of performance.
+But instead,
+Apple's focusing on less abstract and more actionable metrics:
+
+Hang Rate
+: How often is the main / UI thread blocked,
+such that the app is unresponsive to user input?
+
+Launch Time
+: How long does an app take to become usable after the user taps its icon?
+
+Peak Memory & Memory at Suspension
+: How much memory does the app use at its peak
+and just before entering the background?
+
+Disk Writes
+: How often does the app write to disk,
+which --- if you didn't already know --- is a
+[comparatively slow operation](https://people.eecs.berkeley.edu/~rcs/research/interactive_latency.html)
+_(even with the flash storage on an iPhone!)_
+
+## Using MetricKit
+
+From the perspective of an API consumer,
+it's hard to imagine how MetricKit could be easier to incorporate.
+All you need is for some part of your app to serve as
+a metric subscriber
+(an obvious choice is your `AppDelegate`),
+and for it to be added to the shared `MXMetricManager`:
+
+```swift
+import UIKit
+import MetricKit
+
+@UIApplicationMain
+class AppDelegate: UIResponder, UIApplicationDelegate {
+ func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
+ MXMetricManager.shared.add(self)
+ return true
+ }
+
+ func applicationWillTerminate(_ application: UIApplication) {
+ MXMetricManager.shared.remove(self)
+ }
+}
+
+extension AppDelegate: MXMetricManagerSubscriber {
+ func didReceive(_ payloads: [MXMetricPayload]) {
+ <# ... #>
+ }
+}
+```
+
+iOS automatically collects samples while your app is being used,
+and once per day (every 24 hours),
+it'll send an aggregated report with those metrics.
+
+To verify that your `MXMetricManagerSubscriber`
+is having its delegate method called as expected,
+select Simulate MetricKit Payloads from the Debug menu
+while Xcode is running your app.
+
+{% warning %}
+
+The Simulate MetricKit Payloads menu item
+requires the app to be running on an actual device
+and is disabled for Simulator builds.
+
+{% endwarning %}
+
+### Annotating Critical Code Sections with Signposts
+
+In addition to the baseline statistics collected for you,
+you can use the
+[`mxSignpost`](https://developer.apple.com/documentation/metrickit/3214364-mxsignpost) function
+to collect metrics around the most important parts of your code.
+This [signpost](https://developer.apple.com/documentation/os/3019241-os_signpost)-backed API
+captures CPU time, memory, and writes to disk.
+
+For example,
+if part of your app did post-processing on audio streams,
+you might annotate those regions with metric signposts
+to determine the energy and performance impact of that work:
+
+```swift
+let audioLogHandle = MXMetricManager.makeLogHandle(category: "Audio")
+
+func processAudioStream() {
+ mxSignpost(.begin, log: audioLogHandle, name: "ProcessAudioStream")
+ <#...#>
+ mxSignpost(.end, log: audioLogHandle, name: "ProcessAudioStream")
+}
+```
+
+## Creating a Self-Hosted Web Service for Collecting App Metrics
+
+Now that you have this information,
+what do you do with it?
+How do we fill that `<#...#>` placeholder in our implementation of `didReceive(_:)`?
+
+You _could_ pass that along to some paid analytics or crash reporting service,
+_but where's the fun in that_?
+Let's build our own web service to collect these for further analysis:
+
+### Storing and Querying Metrics with PostgreSQL
+
+The `MXMetricPayload` objects received by metrics manager subscribers
+have a convenient
+[`jsonRepresentation()`](https://developer.apple.com/documentation/metrickit/mxmetricpayload/3131907-jsonrepresentation) method
+that generates something like this:
+
+{% capture metrics_json %}
+
+```json
+{
+ "locationActivityMetrics": {
+ "cumulativeBestAccuracyForNavigationTime": "20 sec",
+ "cumulativeBestAccuracyTime": "30 sec",
+ "cumulativeHundredMetersAccuracyTime": "30 sec",
+ "cumulativeNearestTenMetersAccuracyTime": "30 sec",
+ "cumulativeKilometerAccuracyTime": "20 sec",
+ "cumulativeThreeKilometersAccuracyTime": "20 sec"
+ },
+ "cellularConditionMetrics": {
+ "cellConditionTime": {
+ "histogramNumBuckets": 3,
+ "histogramValue": {
+ "0": {
+ "bucketCount": 20,
+ "bucketStart": "1 bars",
+ "bucketEnd": "1 bars"
+ },
+ "1": {
+ "bucketCount": 30,
+ "bucketStart": "2 bars",
+ "bucketEnd": "2 bars"
+ },
+ "2": {
+ "bucketCount": 50,
+ "bucketStart": "3 bars",
+ "bucketEnd": "3 bars"
+ }
+ }
+ }
+ },
+ "metaData": {
+ "appBuildVersion": "0",
+ "osVersion": "iPhone OS 13.1.3 (17A878)",
+ "regionFormat": "US",
+ "deviceType": "iPhone9,2"
+ },
+ "gpuMetrics": {
+ "cumulativeGPUTime": "20 sec"
+ },
+ "memoryMetrics": {
+ "peakMemoryUsage": "200,000 kB",
+ "averageSuspendedMemory": {
+ "averageValue": "100,000 kB",
+ "standardDeviation": 0,
+ "sampleCount": 500
+ }
+ },
+ "signpostMetrics": [
+ {
+ "signpostIntervalData": {
+ "histogrammedSignpostDurations": {
+ "histogramNumBuckets": 3,
+ "histogramValue": {
+ "0": {
+ "bucketCount": 50,
+ "bucketStart": "0 ms",
+ "bucketEnd": "100 ms"
+ },
+ "1": {
+ "bucketCount": 60,
+ "bucketStart": "100 ms",
+ "bucketEnd": "400 ms"
+ },
+ "2": {
+ "bucketCount": 30,
+ "bucketStart": "400 ms",
+ "bucketEnd": "700 ms"
+ }
+ }
+ },
+ "signpostCumulativeCPUTime": "30,000 ms",
+ "signpostAverageMemory": "100,000 kB",
+ "signpostCumulativeLogicalWrites": "600 kB"
+ },
+ "signpostCategory": "TestSignpostCategory1",
+ "signpostName": "TestSignpostName1",
+ "totalSignpostCount": 30
+ },
+ {
+ "signpostIntervalData": {
+ "histogrammedSignpostDurations": {
+ "histogramNumBuckets": 3,
+ "histogramValue": {
+ "0": {
+ "bucketCount": 60,
+ "bucketStart": "0 ms",
+ "bucketEnd": "200 ms"
+ },
+ "1": {
+ "bucketCount": 70,
+ "bucketStart": "201 ms",
+ "bucketEnd": "300 ms"
+ },
+ "2": {
+ "bucketCount": 80,
+ "bucketStart": "301 ms",
+ "bucketEnd": "500 ms"
+ }
+ }
+ },
+ "signpostCumulativeCPUTime": "50,000 ms",
+ "signpostAverageMemory": "60,000 kB",
+ "signpostCumulativeLogicalWrites": "700 kB"
+ },
+ "signpostCategory": "TestSignpostCategory2",
+ "signpostName": "TestSignpostName2",
+ "totalSignpostCount": 40
+ }
+ ],
+ "displayMetrics": {
+ "averagePixelLuminance": {
+ "averageValue": "50 apl",
+ "standardDeviation": 0,
+ "sampleCount": 500
+ }
+ },
+ "cpuMetrics": {
+ "cumulativeCPUTime": "100 sec"
+ },
+ "networkTransferMetrics": {
+ "cumulativeCellularDownload": "80,000 kB",
+ "cumulativeWifiDownload": "60,000 kB",
+ "cumulativeCellularUpload": "70,000 kB",
+ "cumulativeWifiUpload": "50,000 kB"
+ },
+ "diskIOMetrics": {
+ "cumulativeLogicalWrites": "1,300 kB"
+ },
+ "applicationLaunchMetrics": {
+ "histogrammedTimeToFirstDrawKey": {
+ "histogramNumBuckets": 3,
+ "histogramValue": {
+ "0": {
+ "bucketCount": 50,
+ "bucketStart": "1,000 ms",
+ "bucketEnd": "1,010 ms"
+ },
+ "1": {
+ "bucketCount": 60,
+ "bucketStart": "2,000 ms",
+ "bucketEnd": "2,010 ms"
+ },
+ "2": {
+ "bucketCount": 30,
+ "bucketStart": "3,000 ms",
+ "bucketEnd": "3,010 ms"
+ }
+ }
+ },
+ "histogrammedResumeTime": {
+ "histogramNumBuckets": 3,
+ "histogramValue": {
+ "0": {
+ "bucketCount": 60,
+ "bucketStart": "200 ms",
+ "bucketEnd": "210 ms"
+ },
+ "1": {
+ "bucketCount": 70,
+ "bucketStart": "300 ms",
+ "bucketEnd": "310 ms"
+ },
+ "2": {
+ "bucketCount": 80,
+ "bucketStart": "500 ms",
+ "bucketEnd": "510 ms"
+ }
+ }
+ }
+ },
+ "applicationTimeMetrics": {
+ "cumulativeForegroundTime": "700 sec",
+ "cumulativeBackgroundTime": "40 sec",
+ "cumulativeBackgroundAudioTime": "30 sec",
+ "cumulativeBackgroundLocationTime": "30 sec"
+ },
+ "timeStampEnd": "2019-10-22 06:59:00 +0000",
+ "applicationResponsivenessMetrics": {
+ "histogrammedAppHangTime": {
+ "histogramNumBuckets": 3,
+ "histogramValue": {
+ "0": {
+ "bucketCount": 50,
+ "bucketStart": "0 ms",
+ "bucketEnd": "100 ms"
+ },
+ "1": {
+ "bucketCount": 60,
+ "bucketStart": "100 ms",
+ "bucketEnd": "400 ms"
+ },
+ "2": {
+ "bucketCount": 30,
+ "bucketStart": "400 ms",
+ "bucketEnd": "700 ms"
+ }
+ }
+ }
+ },
+ "appVersion": "1.0.0",
+ "timeStampBegin": "2019-10-21 07:00:00 +0000"
+}
+```
+
+{% endcapture %}
+
+{::nomarkdown}
+
+
+Expand for JSON Representation:
+{{ metrics_json | markdownify }}
+
+{:/}
+
+As you can see,
+there's a lot baked into this representation.
+Defining a schema for all of this information would be a lot of work,
+and there's no guarantee that this won't change in the future.
+So instead,
+let's embrace the NoSQL paradigm
+_(albeit responsibly, using [Postgres](https://postgresapp.com))_
+by storing payloads in a [`JSONB` column](https://www.postgresql.org/docs/current/datatype-json.html):
+
+```sql
+CREATE TABLE IF NOT EXISTS metrics (
+ id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
+ payload JSONB NOT NULL
+);
+```
+
+_So easy!_
+
+We can extract individual fields from payloads
+using [JSON operators](https://www.postgresql.org/docs/current/functions-json.html)
+like so:
+
+```sql
+SELECT (payload -> 'applicationTimeMetrics'
+ ->> 'cumulativeForegroundTime')::INTERVAL
+FROM metrics;
+-- interval
+-- ═══════════════════
+-- @ 11 mins 40 secs
+-- (1 row)
+```
+
+{% info %}
+
+The JSON representation of metrics stores measurements for time and memory
+as strings with units (such as `"100 ms"` and `500 kB`).
+In Postgres, you can cast time measurements directly to the
+[`INTERVAL` type](https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-INTERVAL-INPUT),
+however you'll need to create a function to convert to byte counts:
+
+```sql
+CREATE OR REPLACE FUNCTION parse_byte_count (TEXT)
+RETURNS BIGINT
+AS $$
+ SELECT
+ replace(split_part($1, ' ', 1),',','')::BIGINT *
+ CASE split_part($1, ' ', 2)
+ WHEN 'kB' THEN 1000
+ WHEN 'MB' THEN 1000 * 1000
+ WHEN 'GB' THEN 1000 * 1000 * 1000
+ END
+$$ LANGUAGE 'sql' STRICT IMMUTABLE;
+```
+
+{% endinfo %}
+
+#### Advanced: Creating Views
+
+JSON operators in PostgreSQL can be cumbersome to work with ---
+especially for more complex queries.
+One way to help with that is to create a view
+_([materialized](https://www.postgresql.org/docs/current/rules-materializedviews.html) or otherwise)_
+to project the most important information to you
+in the most convenient representation:
+
+```sql
+CREATE VIEW key_performance_indicators AS
+SELECT
+ id,
+ (payload -> 'appVersion') AS app_version,
+ (payload -> 'metaData' ->> 'deviceType') AS device_type,
+ (payload -> 'metaData' ->> 'regionFormat') AS region,
+ (payload -> 'applicationTimeMetrics'
+ ->> 'cumulativeForegroundTime'
+ )::INTERVAL AS cumulative_foreground_time,
+ parse_byte_count(
+ payload -> 'memoryMetrics'
+ ->> 'peakMemoryUsage'
+ ) AS peak_memory_usage_bytes
+FROM metrics;
+```
+
+With views,
+you can perform
+[aggregate queries](https://www.postgresql.org/docs/current/functions-aggregate.html)
+over all of your metrics JSON payloads
+with the convenience of a schema-backed relational database:
+
+```sql
+SELECT avg(cumulative_foreground_time)
+FROM key_performance_indicators;
+-- avg
+-- ══════════════════
+-- @ 9 mins 41 secs
+
+SELECT app_version, percentile_disc(0.5)
+ WITHIN GROUP (ORDER BY peak_memory_usage_bytes)
+ AS median
+FROM key_performance_indicators
+GROUP BY app_version;
+-- app_version │ median
+-- ═════════════╪═══════════
+-- "1.0.1" │ 192500000
+-- "1.0.0" │ 204800000
+```
+
+{% error %}
+
+PostgreSQL doesn't handle camelcase well for table or column names,
+so keep that in mind when using functions like
+[`jsonb_to_record`](https://www.postgresql.org/docs/current/functions-json.html#FUNCTIONS-JSON-PROCESSING-TABLE).
+
+{% enderror %}
+
+### Creating a Web Service
+
+In this example,
+most of the heavy lifting is delegated to Postgres,
+making the server-side implementation rather boring.
+For completeness,
+here are some reference implementations in
+Ruby (Sinatra) and JavaScript (Express):
+
+```ruby
+require 'sinatra/base'
+require 'pg'
+require 'sequel'
+
+class App < Sinatra::Base
+ configure do
+ DB = Sequel.connect(ENV['DATABASE_URL'])
+ end
+
+ post '/collect' do
+ DB[:metrics].insert(payload: request.body.read)
+ status 204
+ end
+end
+```
+
+```javascript
+import express from 'express';
+import { Pool } from 'pg';
+
+const db = new Pool(
+ connectionString: process.env.DATABASE_URL,
+ ssl: process.env.NODE_ENV === 'production'
+);
+
+const app = express();
+app.post('/collect', (request, response) => {
+ db.query('INSERT INTO metrics (payload) VALUES ($1)', [request.body], (error, results) => {
+ if (error) {
+ throw error;
+ }
+
+ response.status(204);
+ })
+});
+
+app.listen(process.env.PORT || 5000)
+```
+
+### Sending Metrics as JSON
+
+Now that we have everything set up,
+the final step is to implement
+the required `MXMetricManagerSubscriber` delegate method `didReceive(_:)`
+to pass that information along to our web service:
+
+```swift
+extension AppDelegate: MXMetricManagerSubscriber {
+ func didReceive(_ payloads: [MXMetricPayload]) {
+ for payload in payloads {
+ let url = URL(string: "https://example.com/collect")!
+
+ var request = URLRequest(url: url)
+ request.httpMethod = "POST"
+ request.httpBody = payload.jsonRepresentation()
+
+ let task = URLSession.shared.dataTask(with: request)
+ task.priority = URLSessionTask.lowPriority
+ task.resume()
+ }
+ }
+}
+```
+
+
+
+When you create something and put it out into the world,
+you lose your direct connection to it.
+That's as true for apps as it is for college radio shows.
+Short of user research studies or
+[invasive ad-tech](https://techcrunch.com/2019/02/06/iphone-session-replay-screenshots/),
+the truth is that
+[we rarely have any clue about how people are using our software](https://xkcd.com/1172/).
+
+
+
+Metrics offer a convenient way to at least make sure that
+things aren't too slow or too draining.
+And though they provide but a glimpse in the aggregate
+of how our apps are being enjoyed,
+it's just enough to help us honor both our creation and our audience
+with a great user experience.
+
+{% asset "articles/metrickit.css" %}
diff --git a/2019-10-31-device-identifiers.md b/2019-10-31-device-identifiers.md
new file mode 100644
index 00000000..20c384f8
--- /dev/null
+++ b/2019-10-31-device-identifiers.md
@@ -0,0 +1,602 @@
+---
+title: Device Identifiers and Fingerprinting on iOS
+author: Mattt
+category: Miscellaneous
+excerpt: >-
+ For every era,
+ there's a monster that embodies the anxieties of the age.
+status:
+ swift: 5.1
+---
+
+For every era,
+there's a monster that embodies the anxieties of the age.
+
+At the dawn of the [Holocene](https://en.wikipedia.org/wiki/Holocene),
+our ancestors traced the contours of shadows cast by the campfire
+as they kept watch over the darkness.
+Once we learned to read the night sky for navigation,
+sailors swapped stories of sea creatures like
+[Leviathan](https://en.wikipedia.org/wiki/Leviathan) and
+[Siren](https://en.wikipedia.org/wiki/Siren_%28mythology%29)
+to describe the dangers of open ocean
+(and the perils to be found on unfamiliar shores).
+
+[Frankenstein's monster](https://en.wikipedia.org/wiki/Frankenstein%27s_monster)
+was as much the creation of Mary Shelley
+as it was a spiritual collaboration with
+[Luigi Galvani](https://en.wikipedia.org/wiki/Luigi_Galvani).
+And Bram Stoker's
+[fictionalized account of the mummy's curse](https://en.wikipedia.org/wiki/The_Jewel_of_Seven_Stars)
+was more a response to the
+[Egyptomania](https://en.wikipedia.org/wiki/Egyptomania)
+and European colonialism
+of the nineteenth century
+than any personal account of the Middle Kingdom.
+
+More recently,
+the ["monster ruins a beach party"](https://en.wikipedia.org/wiki/The_Beach_Girls_and_the_Monster)
+trope of the 1960s
+arose from concerns of teenager morality.
+While the
+[Martians](https://en.wikipedia.org/wiki/The_Day_Mars_Invaded_Earth)
+who invaded those same drive-in double features
+served as a proxy for Cold War fears at the height of the
+[Space Race](https://en.wikipedia.org/wiki/Space_Race).
+
+
+
+All of which begs the question:
+_"What monster best exemplifies our present age?"_
+
+Consider the unnamed monster from the film
+_[It Follows](https://en.wikipedia.org/wiki/It_Follows)_:
+a formless, supernatural being that relentlessly pursues its victims
+anywhere on the planet.
+
+Sounds a bit like the state of
+ad tech
+in 2019, no?
+
+
+
+
+
+This week on NSHipster —
+in celebration of our favorite holiday
+🎃 —
+we're taking a look at the myriad ways that
+you're being tracked on iOS,
+both sanctioned and unsanctioned,
+historically and presently.
+So gather around the campfire,
+and allow us to trace the contours of the unseen, formless monsters
+that stalk us under cover of [Dark Mode](/dark-mode/).
+
+
+
+## The Cynicism of Marketing and Advertising Technology
+
+Contrary to our intuitions about natural selection in the marketplace,
+history is littered with examples of
+inferior-but-better-marketed products winning out over superior alternatives:
+_VHS vs. Betamax_,
+_Windows vs. Macintosh_,
+etc.
+(According to the common wisdom of business folks, at least.)
+Regardless,
+most companies reach a point where
+_“if you build it, they will come”_
+ceases to be a politically viable strategy,
+and someone authorizes a marketing budget.
+
+Marketers are tasked with growing market share
+by identifying and communicating with as many potential customers as possible.
+And many ---
+either out of a genuine belief or formulated as a post hoc rationalization ---
+take the potential benefit of their product
+as a license to flouting long-established customs of personal privacy.
+So they enlist the help of one or more
+advertising firms,
+who promise to maximize their allocated budget and
+provide some accountability for their spending
+by using technology to
+**target**,
+**deliver**, and
+**analyze**
+messaging to consumers.
+
+**Each of these tasks is predicated on a consistent identity,
+which is why advertisers go to such great lengths to track you.**
+
+- Without knowing who you are,
+ marketers have no way to tell if you're a likely or even potential customer.
+- Without knowing where you are,
+ marketers have no way to reach you
+ other than to post ads where they're likely to be seen.
+- Without knowing what you do,
+ marketers have no way to tell if their ads worked
+ or were seen at all.
+
+## Apple-Sanctioned Identifiers
+
+Apple's provided various APIS to facilitate user identification
+for various purposes:
+
+### Universal Identifiers (UDID)
+
+In the early days of iOS,
+Apple provided a `uniqueIdentifier` property on `UIDevice` ---
+affectionately referred to as a
+UDID
+([not to be confused with a UUID](/uuid-udid-unique-identifier/)).
+Although such functionality seems unthinkable today,
+that property existed until iOS 5,
+until it was
+deprecated and replaced by `identifierForVendor` in iOS 6.
+
+### Vendor Identifiers (IDFV)
+
+Starting in iOS 6,
+developers can use the
+`identifierForVendor` property on `UIDevice`
+to generate a unique identifier that's shared across apps and extensions
+created by the same vendor
+(IDFV).
+
+```swift
+import UIKit
+
+let idfv = UIDevice.current.identifierForVendor // BD43813E-CFC5-4EEB-ABE2-94562A6E76CA
+```
+
+{% warning %}
+
+According to [the documentation](https://developer.apple.com/documentation/uikit/uidevice/1620059-identifierforvendor)
+`identifierForVendor` return `nil`
+"after the device has been restarted but before the user has unlocked the device."
+It's unclear when that would be the case,
+but something to keep in mind if your app does anything in the background.
+
+{% endwarning %}
+
+### Advertising Identifiers (IDFA)
+
+Along with `identifierForVendor` came the introduction of a new
+[AdSupport framework](https://developer.apple.com/documentation/adsupport),
+which Apple created to help distinguish
+identification necessary for app functionality
+from anything in the service of advertising.
+
+The resulting
+`advertisingidentifier` property
+(affectionately referred to as
+IDFA by its associates)
+differs from `identifierForVendor`
+by returning the same value for everyone.
+The value can change, for example,
+if the user [resets their Advertising Identifier](https://support.apple.com/en-us/HT205223)
+or erases their device.
+
+```swift
+import AdSupport
+
+let idfa = ASIdentifierManager.shared().advertisingIdentifier
+```
+
+If advertising tracking is limited,
+the property returns a zeroed-out UUID instead.
+
+```swift
+idfa.uuidString == "00000000-0000-0000-0000-000000000000" // true if the user has limited ad tracking
+```
+
+{% info %}
+
+Curiously,
+macOS Mojave introduced a
+[`clearAdvertisingIdentifier()` method](https://developer.apple.com/documentation/adsupport/asidentifiermanager/2998811-clearadvertisingidentifier),
+which appears to create a _"tragedy of the commons"_ situation,
+where a single app could spoil things for everyone else
+_(not that this is a bad thing from the user's perspective!)_
+
+{% endinfo %}
+
+{% warning %}
+
+There's also the curious case of the
+`isAdvertisingTrackingEnabled` property.
+According to
+[the documentation](https://developer.apple.com/documentation/adsupport/asidentifiermanager/1614148-isadvertisingtrackingenabled):
+
+> Check the value of this property
+> before performing any advertising tracking.
+> If the value is false,
+> use the advertising identifier only for the following purposes:
+> frequency capping,
+> attribution,
+> conversion events,
+> estimating the number of unique users,
+> advertising fraud detection,
+> and debugging.
+
+This kind of _"honor system"_ approach compliance is confusing.
+And it makes you wonder what kind of usage
+_wouldn't_ fall within these broad allowances.
+
+If you have any insight into how this is policed,
+[drop us a line](https://twitter.com/nshipster) ---
+we'd love to hear more.
+
+{% endwarning %}
+
+### DeviceCheck
+
+`identifierForVendor` and `advertisingIdentifier`
+provide all the same functionality as the `uniqueIdentifier` property
+they replaced in iOS 6,
+save for one:
+the ability to persist across device resets and app uninstalls.
+
+In iOS 11,
+Apple quietly introduced the
+[DeviceCheck framework](https://developer.apple.com/documentation/devicecheck),
+which allows developers to assign two bits of information
+that are persisted by Apple
+**until the developer manually removes them**.
+
+Interacting with the DeviceCheck framework should be familiar to
+anyone familiar with [APNS](/apns-device-tokens):
+after setting things up on App Store Connect and your servers,
+the client generates tokens on the device,
+which are sent to your servers to set or query two bits of information:
+
+```swift
+import DeviceCheck
+
+let device = DCDevice.current
+if device.isSupported {
+ device.generateToken { data, error in
+ if let token = data?.base64EncodedString() {
+ <#send token to your server#>
+ }
+ }
+}
+```
+
+Based on the device token and other information sent by the client,
+the server tells Apple to set each bit value
+by sending a JSON payload like this:
+
+```json
+{
+ "device_token": "QTk4QkFDNEItNTBDMy00Qjc5LThBRUEtMDQ5RTQzRjNGQzU0Cg==",
+ "transaction_id": "D98BA630-E225-4A2F-AFEC-BE3A3D591708",
+ "timestamp": 1572531720,
+ "bit0": true,
+ "bit1": false
+}
+```
+
+To retrieve those two bits at a later point in time,
+the server sends a payload without `bit0` and `bit1` fields:
+
+```json
+{
+ "device_token": "QTk4QkFDNEItNTBDMy00Qjc5LThBRUEtMDQ5RTQzRjNGQzU0Cg==",
+ "transaction_id": "D98BA630-E225-4A2F-AFEC-BE3A3D591708",
+ "timestamp": 1572532500
+}
+```
+
+If everything worked,
+Apple's servers would respond with a `200` status code
+and the following JSON payload:
+
+```json
+{
+ "bit0" : true
+ "bit1" : false,
+ "last_update_time" : "2019-10"
+}
+```
+
+{% error %}
+
+Apple allegedly created the DeviceCheck framework
+to meet the needs of Uber
+in limiting the abuse of promotional codes.
+Although DeviceCheck proports to store _only_ two bits of information
+(just enough to, for example,
+determine whether a device has ever been used to create an account
+and whether the device was ever associated with fraudulent activity),
+we have (admittedly vague) concerns that the timestamp, even with truncation,
+could be exploited to store more than two bits of information.
+
+{% enderror %}
+
+## Fingerprinting in Today's iOS
+
+Despite these affordances by Apple,
+advertisers have continued to work to circumvent user privacy protections
+and use any and all information at their disposal
+to identify users by other means.
+
+Over the years,
+Apple's restricted access to information about
+device hardware,
+[installed apps](https://developer.apple.com/documentation/uikit/uiapplication/1622952-canopenurl),
+[nearby WiFi networks](https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_networking_wifi-info).
+They've required apps to request permission to
+get your current location,
+access your camera and microphone,
+flip through your contacts, and
+find and connect to Bluetooth accessories.
+They've taken bold steps to [prevent user tracking in Safari](https://webkit.org/blog/8828/intelligent-tracking-prevention-2-2/).
+
+For lack of this information,
+companies have had to get creative,
+looking to forge unique identities from the scraps of what's still available.
+This process of identification by a combination of external factors
+is known as [fingerprinting](https://en.wikipedia.org/wiki/Fingerprint_%28computing%29).
+
+The unfortunate reality is that we can be uniquely identified
+by vanishingly small amounts of information.
+For example,
+individuals within a population can be singled out by as few as
+four timestamped coordinates
+{% cite de_montjoye_2013 %}
+or little more than a birthday and a ZIP code
+{% cite sweeney_2000 %}.
+
+Every WWDC since 2012 has featured a session about Privacy,
+but the only mention of fingerprinting specifically was
+[a brief discussion in 2014](https://asciiwwdc.com/2014/sessions/715?q=fingerprinting)
+about how to avoid doing it.
+
+By our count,
+a determined party could use conventional, unrestricted APIs
+to generate a few dozen bits of randomness:
+
+### Locale Information (~36 bits)
+
+Locale information is the greatest source of identifying information
+on Apple platforms.
+The combination of your
+preferred languages, region, calendar, time zone,
+and which keyboards you have installed
+say a lot about who you are ---
+especially if you have less conventional preferences.
+
+```swift
+import Foundation
+
+Locale.current.languageCode
+log2(Double(Locale.isoLanguageCodes.count)) // 9.217 bits
+
+Locale.current.regionCode
+log2(Double(Locale.isoRegionCodes.count)) // 8 bits
+
+Locale.current.calendar.identifier
+// ~2^4 (16) Calendars
+
+TimeZone.current.identifier
+log2(Double(TimeZone.knownTimeZoneIdentifiers.count)) // 8.775 bits
+
+UserDefaults.standard.object(forKey: "AppleKeyboards")
+// ~2^6 (64) iOS keyboards
+```
+
+{% info %}
+
+We recently [Tweeted](https://twitter.com/mattt/status/1175817188646612992)
+about apps having unrestricted access to emoji keyboard information.
+We've since been informed that Apple is investigating the issue.
+
+{% endinfo %}
+
+### Accessibility (~10 bits)
+
+Accessibility preferences also provide a great deal of information,
+with each individual setting contributing a single potential bit:
+
+```swift
+UIAccessibility.isBoldTextEnabled
+UIAccessibility.isShakeToUndoEnabled
+UIAccessibility.isReduceMotionEnabled
+UIAccessibility.isDarkerSystemColorsEnabled
+UIAccessibility.isReduceTransparencyEnabled
+UIAccessibility.isAssistiveTouchRunning
+```
+
+Of the approximately ~25% of users who take advantage of
+[Dynamic Type](https://developer.apple.com/documentation/uikit/uifont/scaling_fonts_automatically)
+by configuring a preferred font size,
+that selection may also be used to fingerprint you:
+
+```swift
+let application = UIApplication.shared
+application.preferredContentSizeCategory
+```
+
+### Hardware Information (~5 or ~6 bits)
+
+Although most of the juiciest bits have been locked down
+in OS updates over the years,
+there's just enough to contribute a few more bits for purposes of identification.
+
+On iOS,
+you can get the current model and amount of storage of a user's device:
+
+```swift
+import UIKit
+
+let device = UIDevice.current
+device.name // "iPhone 11 Pro"
+
+let fileManager = FileManager.default
+if let path = fileManager.urls(for: .libraryDirectory, in: .systemDomainMask).last?.path,
+ let systemSize = try? fileManager.attributesOfFileSystem(forPath: path)[.systemSize] as? Int
+{
+ Measurement(value: Double(systemSize), unit: .bytes)
+ .converted(to: .gigabytes) // ~256GB
+}
+```
+
+With [14 supported iOS devices](https://support.apple.com/guide/iphone/supported-models-iphe3fa5df43/ios),
+most having 3 configurations each,
+let's say that this contributes about 32 possibilities, or 5 bits.
+
+You can go a few steps further on macOS,
+to further differentiate hardware by its processor count and amount of RAM:
+
+```swift
+processInfo.processorCount // 8
+
+Measurement(value: Double(processInfo.physicalMemory),
+ unit: .bytes)
+ .converted(to: .gigabytes) // 16GB
+```
+
+It's hard to get a sense of
+[how many different Mac models are in use](https://everymac.com/systems/by_capability/minimum-macos-supported.html),
+but a reasonable estimate would be on the order of 26 or 27.
+
+### Cellular Network (~2 bits)
+
+Knowing whether someone's phone is on Verizon or Vodafone
+can also be factored into a fingerprint.
+You can use the `CTTelephonyNetworkInfo` class from the
+[CoreTelephony framework](https://developer.apple.com/documentation/coretelephony)
+to lookup the providers for devices with cellular service:
+
+```swift
+import CoreTelephony
+
+let networkInfo = CTTelephonyNetworkInfo()
+let carriers = networkInfo.serviceSubscriberCellularProviders?.values
+carriers?.map { ($0.mobileNetworkCode, $0.mobileCountryCode) }
+```
+
+The number of providers varies per country,
+but using the 4 major carriers in United States
+as a guideline,
+we can say carrier information would contribute about 2 bits
+(or more if you have multiple SIM cards installed).
+
+## Communication Preferences (2 bits)
+
+More generally,
+even knowing whether someone can send texts or email at all
+can be factored into a fingerprint.
+This information can be gathered without permissions via
+the [MessageUI framework](https://developer.apple.com/documentation/messageui).
+
+```swift
+import MessageUI
+
+MFMailComposeViewController.canSendMail()
+MFMessageComposeViewController.canSendText()
+```
+
+## Additional Sources of Identifying Information
+
+If the use of digital fingerprinting seems outlandish,
+that's just scratching the surface of how companies and researchers
+have figured out how to circumvent your privacy.
+
+### GeoIP and Relative Network Speeds
+
+Although access to geolocation through conventional APIs
+requires explicit authorization,
+third parties may be able to get a general sense of where you are in the world
+based on how you access the Internet.
+
+[Geolocation by source IP address](https://ipinfo.io)
+is used extensively for things like region locking and localization.
+You could also combine this information with
+[ping-time measurements](https://developer.apple.com/library/archive/samplecode/SimplePing/Introduction/Intro.html#//apple_ref/doc/uid/DTS10000716)
+to hosts in known locations
+to get a more accurate pinpoint on location {% cite weinberg_2018 %}:
+
+```terminal
+ping -c 5 99.24.18.13 # San Francisco, USA
+
+--- 99.24.18.13 ping statistics ---
+5 packets transmitted, 5 packets received, 0.0% packet loss
+round-trip min/avg/max/stddev = 11.900/12.184/12.895/0.380 ms
+
+ping -c 5 203.47.10.37 # Adelaide, Australia
+
+--- 203.47.10.37 ping statistics ---
+5 packets transmitted, 5 packets received, 0.0% packet loss
+round-trip min/avg/max/stddev = 202.122/202.433/203.436/0.504 ms
+```
+
+### Battery Health
+
+It's unclear whether this is a concern in iOS,
+but depending on how precise the results of `UIDevice` battery APIs are,
+you may be able to use them to identify a device by its battery health.
+{% cite olejnik_2015 %}
+
+```swift
+var timestampedBatteryLevels: [(Date, Float)] = []
+
+if UIDevice.current.isBatteryMonitoringEnabled {
+ timestampedBatteryLevels.append((Date(), UIDevice.current.batteryLevel))
+}
+```
+
+{% info %}
+
+For this reason,
+battery level APIs were
+[removed in Firefox 55](https://developer.mozilla.org/en-US/docs/Web/API/Battery_Status_API).
+
+If this seems outlandish,
+consider that Apple recently released a security update for iOS after researchers
+demonstrated that small discrepancies in gyroscope calibration settings
+could be used to uniquely identify devices.
+{% cite zhang_2019 %}
+
+{% endinfo %}
+
+### And so on...
+
+Everything from your heartbeat, to your gait, to your
+[butt shape](https://www.wired.co.uk/article/surveillance-technology-biometrics)
+seem capable of leaking your identity.
+It can all be quite overwhelming.
+
+I mean,
+if a motivated individual can find your home address by
+[cross-referencing the reflection in your eyes against Google Street view](https://www.theverge.com/2019/10/11/20910551/stalker-attacked-pop-idol-reflection-pupils-selfies-videos-photos-google-street-view-japan),
+how can we even stand a chance out there?
+
+
+
+Much as we may bemoan the current duopoly of mobile operating systems,
+we might take some solace in the fact that
+at least one of the players actually cares about user privacy.
+Though it's unclear whether that's a fight that can ever be won.
+
+At times,
+our fate of being tracked and advertised to
+may seem as inevitable as the victims in _It Follows_.
+
+But let's not forget that,
+as technologists, as people with a voice,
+we're in a position to fight back.
+
+
diff --git a/2019-11-04-message-id.md b/2019-11-04-message-id.md
new file mode 100644
index 00000000..d7a7c1eb
--- /dev/null
+++ b/2019-11-04-message-id.md
@@ -0,0 +1,305 @@
+---
+title: Message-ID and Mail.app Deep Linking on iOS and macOS
+author: Mattt
+category: Miscellaneous
+excerpt: >-
+ Privacy enhancements to privacy in recent versions of iOS
+ have afforded users much greater control of how
+ their information is shared.
+ However, these improvements have come at a slight cost
+ to certain onboarding flows.
+ Rather than attempting to work around Apple's privacy protections
+ with techniques like device fingerprinting,
+ we can instead rely on a longtime system integration with email.
+status:
+ swift: 5.1
+---
+
+[Last week](/device-identifiers/),
+we concluded our discussion of device identifiers
+with a brief foray into the ways apps use
+[device fingerprinting](/device-identifiers/#fingerprinting-in-todays-ios)
+to work around Apple's provided APIs
+to track users without their consent or awareness.
+In response,
+a few readers got in touch to explain why
+their use of fingerprinting
+to bridge between Safari and their native app was justified.
+
+At WWDC 2018,
+Apple [announced](https://developer.apple.com/videos/play/wwdc2017/702/) that
+starting in iOS 11 apps would no longer have access to a shared cookie store.
+Previously,
+if a user was logged into a website in Safari on iOS
+and installed the native app,
+the app could retrieve the session cookie from an `SFSafariViewController`
+to log the user in automatically.
+The change was implemented as a countermeasure against
+user tracking by advertisers and other third parties,
+but came at the expense of certain onboarding flows used at the time.
+
+While
+[iCloud Keychain](https://support.apple.com/en-us/HT204085),
+[Shared Web Credentials](https://developer.apple.com/documentation/security/shared_web_credentials),
+[Password Autofill](https://developer.apple.com/documentation/security/password_autofill),
+[Universal Links](https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content), and
+[Sign in with Apple](https://developer.apple.com/documentation/signinwithapplejs/)
+have gone a long way to
+minimize friction for account creation and authentication,
+there are still a few use cases that aren't entirely covered by these new features.
+
+In this week's article,
+we'll endeavor to answer one such use case, specifically:\\
+**How to do seamless "passwordless" authentication via email on iOS.**
+
+
+
+## Mail and Calendar Integrations on Apple Platforms
+
+When you view an email on macOS and iOS,
+Mail underlines
+[detected dates and times](/nsdatadetector/).
+You can interact with them to create a new calendar event.
+If you open such an event in Calendar,
+you'll see a "Show in Mail" link in its extended details.
+Clicking on this link takes you back to the original email message.
+
+This functionality goes all the way back to the launch of the iPhone;
+its inclusion in that year's
+[Mac OS X release (Leopard)](https://daringfireball.net/2007/12/message_urls_leopard_mail)
+would mark the first of many mobile features
+that would make their way to the desktop.
+
+If you were to copy this "magic" URL to the pasteboard
+and view in a text editor,
+you'd see something like this:
+
+```swift
+"message:%3C1572873882024.NSHIPSTER%40mail.example.com%3E"
+```
+
+Veteran iOS developers will immediately recognize this to use a
+[custom URL scheme](https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app).
+And the web-savvy among them could percent-decode the host
+and recognize it to be something akin to an email address, but not.
+
+_So if not an email address, what are we looking at here?_\\
+It's a different email field known as a Message-ID.
+
+## Message-ID
+
+[RFC 5322 §3.6.4](https://tools.ietf.org/html/rfc5322#section-3.6.4)
+prescribes that every email message SHOULD
+have a "Message-ID:" field
+containing a single unique message identifier.
+The syntax for this identifier is essentially an email address
+with enclosing angle brackets (`<>`).
+
+Although the specification contains no normative guidance
+for what makes for a good Message-ID,
+there's a
+[draft IETF document](https://tools.ietf.org/html/draft-ietf-usefor-message-id-01)
+from 1998 that holds up quite well.
+
+Let's take a look at how to do this in Swift:
+
+### Generating a Random Message ID
+
+The first technique described in the aforementioned document
+involves generating a random Message ID with a 64-bit nonce,
+which is prepended by a timestamp to further reduce the chance of collision.
+We can do this rather easily
+using the random number generator APIs built-in to Swift 5
+and the
+[`String(_:radix:uppercase:)` initializer](https://developer.apple.com/documentation/swift/string/2997127-init):
+
+```swift
+import Foundation
+
+let timestamp = String(Int(Date().timeIntervalSince1970 * 1000))
+let nonce = String(UInt64.random(in: 0.."
+```
+
+We could then save the generated Message-ID with the associated record
+in order to link to it later.
+However,
+in many cases,
+a simpler alternative would be to make the Message ID deterministic,
+computable from its existing state.
+
+### Generating a Deterministic Message ID
+
+Consider a record structure that conforms to
+[`Identifiable` protocol](/identifiable/)
+and whose associated `ID` type is a
+[UUID](/uuid-udid-unique-identifier/).
+You could generate a Message ID like so:
+
+```swift
+import Foundation
+
+func messageID(for value: Value, domain: String) -> String
+ where Value: Identifiable, Value.ID == UUID
+{
+ return "<\(value.id.uuidString)@\(domain)>"
+}
+```
+
+{% info %}
+
+For lack of a persistent identifier
+(or any other distinguishing features),
+you might instead use a digest of the message body itself
+to generate a Message ID.
+Here's an example implementation that uses the new
+[CryptoKit framework](https://developer.apple.com/documentation/cryptokit):
+
+```swift
+import Foundation
+import CryptoKit
+
+let body = #"""
+Lorem ipsum dolor sit amet, consectetur adipiscing elit,
+sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
+nisi ut aliquip ex ea commodo consequat.
+"""#
+
+let digest = Data(SHA256.hash(data: body.data(using: .utf8)!))
+ .map { String($0, radix: 16, uppercase: true) }
+ .joined()
+
+let domain = "ADF"
+"<\(digest)@\(domain)>"
+// ""
+```
+
+{% endinfo %}
+
+## Mobile Deep Linking
+
+The stock Mail client on both iOS and macOS
+will attempt to open URLs with the custom `message:` scheme
+by launching to the foreground
+and attempting to open the message
+with the encoded Message-ID field.
+
+### Generating a Mail Deep Link with Message ID
+
+With a Message-ID in hand,
+the final task is to create a deep link that you can use to
+open Mail to the associated message.
+The only trick here is to
+[percent-encode](https://en.wikipedia.org/wiki/Percent-encoding)
+the Message ID in the URL.
+You could do this with the
+[`addingPercentEncoding(withAllowedCharacters:)` method](/character-set/),
+but we prefer to delegate this all to [`URLComponents`](/nsurl/) instead ---
+which has the further advantage of being able to
+construct the URL full without a
+[format string](/expressiblebystringinterpolation/).
+
+```swift
+import Foundation
+
+var components = URLComponents()
+components.scheme = "message"
+components.host = MessageID
+components.string!
+// "message://%3C1572873882024.NSHIPSTER%40mail.example.com%3E"
+```
+
+{% info %}
+
+As far as we can tell,
+the presence or absence of a double slash after the custom `message:` scheme
+doesn't have any impact on Mail deep links resolution.
+
+{% endinfo %}
+
+### Opening a Mail Deep Link
+
+If you open a `message:` URL on iOS
+and the linked message is readily accessible from the INBOX
+of one of your accounts,
+Mail will launch immediately to that message.
+If the message isn't found,
+the app will launch and asynchronously load the message in the background,
+opening it once it's available.
+
+{% error %}
+
+
+
+
+
+
+
+
+
+
+> **The operation couldn’t be completed. (MCMailErrorDomain error 1030.)**
+> Mail was unable to open the URL “message://%3C1572873882024.NSHIPSTER%40mail.example.com%3E”.
+
+
+
+
+
+By contrast,
+attempting to open a Mail deep link on macOS to a message that isn't yet loaded
+causes an alert modal to display.
+For this reason,
+we recommend using Mail deep links on iOS only.
+
+{% enderror %}
+
+As an example,
+[Flight School](https://flight.school/)
+does this with passwordless authentication system.
+To access electronic copies of your books,
+you enter the email address you used to purchase them.
+Upon form submission,
+users on iOS will see a deep link to open the Mail app
+to the email containing the "magic sign-in link" ✨.
+
+Other systems might use Message-ID
+to streamline passwordless authentication for their native app or website
+by way of
+[Universal Links](https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content),
+or incorporate it as part of a
+2FA strategy
+(since [SMS is no longer considered to be secure for this purpose](https://pages.nist.gov/800-63-3/sp800-63b.html#ooba)).
+
+{% info %}
+
+If you're using Rails for your web application,
+[ActiveMailer interceptors](https://guides.rubyonrails.org/action_mailer_basics.html#intercepting-and-observing-emails)
+provide a convenient way to inject `Message-ID` fields
+for passwordless authentication flows.
+
+{% endinfo %}
+
+
+
+Unlike so many private integrations on Apple platforms,
+which remain the exclusive territory of first-party apps,
+the secret sauce of "Show in Mail" is something we can all get in on.
+Although undocumented,
+the feature is unlikely to be removed anytime soon
+due to its deep system integration and roots in fundamental web standards.
+
+At a time when everyone from
+[browser vendors](https://amp.dev) and
+[social media companies](https://facebook.com) to
+[governments](https://en.wikipedia.org/wiki/Internet_censorship) --- and even
+Apple itself, at times ---
+seek to dismantle the open web and control what we can see and do,
+it's comforting to know that email,
+[nearly 50 years on](http://openmap.bbn.com/~tomlinso/ray/mistakes.html),
+remains resolute in its capacity to keep the Internet free and decentralized.
+
+{% asset "articles/message-id.css" %}
diff --git a/2019-11-12-secrets.md b/2019-11-12-secrets.md
new file mode 100644
index 00000000..d7691e38
--- /dev/null
+++ b/2019-11-12-secrets.md
@@ -0,0 +1,426 @@
+---
+title: Secret Management on iOS
+author: Mattt
+category: Miscellaneous
+excerpt: >-
+ One of the great unsolved questions in iOS development is,
+ _"How do I store secrets securely on the client?"_
+status:
+ swift: 5.1
+---
+
+One of the great unsolved questions in iOS development is,
+_"How do I store secrets securely on the client?"_
+
+Baked into this question is the assumption that,
+without taking proper measures,
+those secrets will be leaked in one way or another ---
+either in source revision control (GitHub and the like),
+from analysis tools running on `.ipa` files distributed through the App Store,
+or some other way.
+
+Although credential security is often a second-tier concern _at best_,
+there's a large and growing body of academic work
+that compels us, as developers, to take these threats seriously.
+
+For instance,
+researchers at North Carolina State University
+[found](https://www.ndss-symposium.org/ndss-paper/how-bad-can-it-git-characterizing-secret-leakage-in-public-github-repositories/)
+that thousands of API keys, multi-factor secrets, and other sensitive credentials
+are leaked every day on GitHub.
+{% cite meli_2019 %}
+Another paper published in 2018
+[found](https://ieeexplore.ieee.org/abstract/document/8719525/authors#authors)
+SDK credential misuse in 68 out of a sample of 100 popular iOS apps.
+{% cite wen_2018 %}
+
+This week on NSHipster,
+we're shamelessly invoking
+[a meme from 2017](https://knowyourmeme.com/memes/galaxy-brain)
+to disclose progressively more sophisticated countermeasures
+for keeping your secrets safe.
+
+If your app is among the myriad others out there with an embedded
+Twitter access token,
+AWS Key ID,
+or any other manner of credential,
+read on and expand your mind.
+
+
+
+## 🧠 Normal Brain Hard-Code Secrets in Source Code
+
+Imagine an app that communicates with a web application
+that requires an API key (a secret)
+to be attached to outbound requests
+for purposes of authentication.
+This integration could be managed entirely by a third-party framework,
+or it might be brokered with a `URLSession` you set up in `AppDelegate`.
+In either case,
+there's a question of how and where to store the required API key.
+
+Let's consider the consequences of embedding the secret in code
+as a string literal:
+
+```swift
+enum Secrets {
+ static let apiKey = "6a0f0731d84afa4082031e3a72354991"
+}
+```
+
+When you archive your app for distribution,
+all traces of this code appear to vanish.
+The act of compilation transforms
+human-readable text into machine-executable binary,
+leaving no trace of the secret... _right_?
+
+_Not quite._
+
+Using a reverse-engineering tool
+like [Radare2](https://rada.re),
+that secret is plainly visible from inside the compiled executable.
+You can prove this to yourself with your own project by
+selecting "Generic iOS Device" in the active scheme in your own project,
+creating an archive (Product > Archive),
+and inspecting the resulting `.xcarchive` bundle:
+
+```terminal
+$ r2 ~/Developer/Xcode/Archives/<#...#>.xcarchive/Products/Applications/Swordfish.app/Swordfish
+[0x1000051fc]> iz
+[Strings]
+Num Paddr Vaddr Len Size Section Type String
+000 0x00005fa0 0x100005fa0 30 31 (3.__TEXT.__cstring) ascii _TtC9Swordfish14ViewController
+001 0x00005fc7 0x100005fc7 13 14 (3.__TEXT.__cstring) ascii @32@0:8@16@24
+002 0x00005fe0 0x100005fe0 36 37 (3.__TEXT.__cstring) ascii 6a0f0731d84afa4082031e3a72354991
+<#...#>
+```
+
+While a typical user downloading your app from the App Store
+would never even think to try opening the `.ipa` on their device,
+there are plenty of individuals and organizations whomst'd've
+carved out a niche in the mobile app industry
+analyzing app payloads for analytics and security purposes.
+And even if the majority of these firms are above-board,
+there's likely to be a few bots out there
+combing through apps just to pick out hard-coded credentials.
+
+Much of that is just conjecture,
+but here's what we know for sure:
+
+If you hard-code secrets in source code,
+they live forever in source control ---
+and forever's a long time when it comes to keeping a secret.
+All it takes is one misconfiguration to your repo permissions
+or a data breach with your service provider
+for everything to be out in the open.
+And as one human working with many other humans,
+chances are good that someone, at some point, will eventually mess up.
+
+Not to put too fine a point on it, but:
+
+{% error %}
+
+Don't commit secrets in source code.
+
+{% enderror %}
+
+To quote Benjamin Franklin:
+
+> If you would keep your secret from an enemy, tell it not to a friend.
+
+## 🧠 Big Brain Store Secrets in Xcode Configuration and Info.plist
+
+In [a previous article](/xcconfig/),
+we described how you could use `.xcconfig` files to
+externalize configuration from code
+[according to 12-Factor App best practices](https://12factor.net/config).
+When reified through `Info.plist`,
+these build settings can serve as makeshift
+[`.env` files](https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/osmanagement/env_file.html):
+
+```
+// Development.xcconfig
+API_KEY = 6a0f0731d84afa4082031e3a72354991
+
+// Release.xcconfig
+API_KEY = d9b3c5d63229688e4ddbeff6e1a04a49
+```
+
+So long as these files are kept out of source control,
+this approach solves the problem of leaking credentials in code.
+And at first glance,
+it would appear to remediate our concern about leaking to static analysis:
+
+If we fire up our l33t hax0r tools
+and attenuate them to our executable as before
+(`izz` to list strings binary,
+`~6a0f07` to filter using the first few characters of our secret),
+our search comes up empty:
+
+```terminal
+$ r2 ~/Developer/Xcode/Archives/<#...#>.xcarchive/Products/Applications/Swordfish.app/Swordfish
+[0x100005040]> izz~6a0f
+[0x100005040]>
+```
+
+But before you start celebrating,
+you might want to reacquaint yourself with the rest of the app payload:
+
+```terminal
+$ tree <#...#>/Swordfish.app
+├── Base.lproj
+│ ├── LaunchScreen.storyboardc
+│ │ ├── 01J-lp-oVM-view-Ze5-6b-2t3.nib
+│ │ ├── Info.plist
+│ │ └── UIViewController-01J-lp-oVM.nib
+│ └── Main.storyboardc
+│ ├── BYZ-38-t0r-view-8bC-Xf-vdC.nib
+│ ├── Info.plist
+│ └── UIViewController-BYZ-38-t0r.nib
+├── Info.plist
+├── PkgInfo
+├── Swordfish
+├── _CodeSignature
+│ └── CodeResources
+└── embedded.mobileprovision
+```
+
+That `Info.plist` file we used to store our secrets?
+Here it is, packaged right alongside our executable.
+
+By some measures,
+this approach might be considered _less_ secure than writing secrets in code,
+because the secret is accessible directly from the payload,
+without any fancy tooling.
+
+```terminal
+$ plutil -p <#...#>/Swordfish.app/Info.plist
+{
+ "API_KEY" => "6a0f0731d84afa4082031e3a72354991"
+<#...#>
+```
+
+
+
+There doesn't seem to be a way to configure the environment
+when a user launches your app on their device.
+And as we saw in the previous section,
+using `Info.plist` as a clearinghouse for secrets qua build settings
+is also a no-go.
+
+But maybe there's another way we could capture our environment in code...
+
+## 🧠 Cosmic Brain Obfuscate Secrets Using Code Generation
+
+A while back,
+we wrote about [GYB](/swift-gyb/),
+a code generation tool used in the Swift standard library.
+Although that article focuses on eliminating boilerplate code,
+GYB's metaprogramming capabilities go far beyond that.
+
+For example,
+we can use GYB to pull environment variables into generated code:
+
+```terminal
+$ API_KEY=6a0f0731d84afa4082031e3a72354991 \
+gyb --line-directive '' <<"EOF"
+%{ import os }%
+let apiKey = "${os.environ.get('API_KEY')}"
+EOF
+
+let apiKey = "6a0f0731d84afa4082031e3a72354991"
+```
+
+Generating _(and not committing)_ Swift files from GYB while
+pulling secrets from environment variables
+solves the problem of leaking credentials in source code,
+but it fails to guard against common static analysis tools.
+However,
+we can use a combination of Swift and Python code (via GYB)
+to obfuscate secrets in a way that's more difficult to reverse-engineer.
+
+For example, here's an (admittedly crude) solution
+that implements an XOR cipher
+using a salt that's generated randomly each time:
+
+```swift
+// Secrets.swift.gyb
+%{
+import os
+
+def chunks(seq, size):
+ return (seq[i:(i + size)] for i in range(0, len(seq), size))
+
+def encode(string, cipher):
+ bytes = string.encode("UTF-8")
+ return [ord(bytes[i]) ^ cipher[i % len(cipher)] for i in range(0, len(bytes))]
+}%
+enum Secrets {
+ private static let salt: [UInt8] = [
+ %{ salt = [ord(byte) for byte in os.urandom(64)] }%
+ % for chunk in chunks(salt, 8):
+ ${"".join(["0x%02x, " % byte for byte in chunk])}
+ % end
+ ]
+
+ static var apiKey: String {
+ let encoded: [UInt8] = [
+ % for chunk in chunks(encode(os.environ.get('API_KEY'), salt), 8):
+ ${"".join(["0x%02x, " % byte for byte in chunk])}
+ % end
+ ]
+
+ return decode(encoded, cipher: salt)
+ }
+
+ <#...#>
+}
+```
+
+Secrets are pulled from the environment and encoded by a Python function
+before being included in the source code as `[UInt8]` array literals.
+Those encoded values are then run through an equivalent Swift function
+to retrieve the original value
+without exposing any secrets directly in the source.
+
+The resulting code looks something like this:
+
+```swift
+// Secrets.swift
+enum Secrets {
+ private static let salt: [UInt8] = [
+ 0xa2, 0x00, 0xcf, <#...#>, 0x06, 0x84, 0x1c,
+ ]
+
+ static var apiKey: String {
+ let encoded: [UInt8] = [
+ 0x94, 0x61, 0xff, <#...#> 0x15, 0x05, 0x59,
+ ]
+
+ return decode(encoded, cipher: salt)
+ }
+
+ static func decode(_ encoded: [UInt8], cipher: [UInt8]) -> String {
+ String(decoding: encoded.enumerated().map { (offset, element) in
+ element ^ cipher[offset % cipher.count]
+ }, as: UTF8.self)
+ }
+}
+
+Secrets.apiKey // "6a0f0731d84afa4082031e3a72354991"
+```
+
+{% info %}
+
+If you're using [CocoaPods](/cocoapods/),
+you might be interested in the
+[CocoaPods Keys plugin](https://github.com/orta/cocoapods-keys),
+which also encodes secrets with code generation
+(albeit without any obfuscation).
+
+{% endinfo %}
+
+While [security through obscurity](https://en.wikipedia.org/wiki/Security_through_obscurity)
+may be theoretically unsound,
+[it can be an effective solution in practice](https://ieeexplore.ieee.org/abstract/document/8449256).
+{% cite wang_2018 %}
+
+As the old saying goes:
+
+> You don’t have to outrun a bear to be safe.
+> You just have to outrun the guy next to you.
+
+## 🧠 Galaxy Brain Don't Store Secrets On-Device
+
+No matter how much we obfuscate a secret on the client,
+it's only a matter of time before the secret gets out.
+Given enough time and sufficient motivation,
+an attacker will be able to reverse-engineer
+whatever you throw their way.
+
+The only _true_ way to keep secrets in mobile apps
+is to store them on the server.
+
+Following this tack,
+our imagined plot shifts from an _Oceans 11_-style heist movie
+to a high-stakes _Behind Enemy Lines_ escort mission movie.
+_(Your mission:
+Transfer a payload from the server
+and store it in the
+[Secure Enclave](https://developer.apple.com/documentation/security/certificate_key_and_trust_services/keys/storing_keys_in_the_secure_enclave)
+without an attacker compromising it.)_
+
+If you don't have a server that you can trust,
+there are a few Apple services that can serve as a transport mechanism:
+
+- [On-Demand Resources](https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/On_Demand_Resources_Guide/index.html):
+ Download a [data asset](/nsdataasset/) containing a plain-text secret.
+- [CloudKit Database](https://developer.apple.com/documentation/cloudkitjs/cloudkit/database):
+ Set secrets in a private database in your CloudKit Dashboard.
+ _(Bonus: Subscribe to changes for automatic credential rolling functionality)_
+- [Background Updates](https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/pushing_background_updates_to_your_app):
+ Push secrets silently to clients as soon as they come online via
+ [APNS](/apns-device-tokens/).
+
+Then again,
+once your secret reaches the Secure Enclave,
+it'll be sent right back out with the next outbound request
+for which it's used.
+It's not enough to get it right once;
+security is a practice.
+
+Or put another way:
+
+> A secret in Secure Enclave is safe,
+> but that is not what secrets are for.
+
+## 🧠 Universe Brain Client Secrecy is Impossible
+
+There's no way to secure secrets stored on the client.
+Once someone can run your software on their own device,
+it's game over.
+
+And maintaining a secure, closed communications channel between client and server
+incurs an immense amount of operational complexity ---
+assuming it's possible in the first place.
+
+Perhaps Julian Assange said it best:
+
+> The only way to keep a secret is to never have one.
+
+
+
+**Rather than looking at client secret management as a problem to be solved,
+we should see it instead as an anti-pattern to be avoided.**
+
+What is an `API_KEY` other than an insecure, anonymous authentication mechanism, anyway?
+It's a blank check that anyone can cash,
+a persistent liability the operational integrity of your business.
+
+Any third-party SDK that's configured with a client secret is insecure by design.
+If your app uses any SDKs that fits this description,
+you should see if it's possible to move the integration to the server.
+Barring that,
+you should take time to understand the impact of a potential leak
+and consider whether you're willing to accept that risk.
+If you deem the risk to be substantial,
+it wouldn't be a bad idea to look into ways to obfuscate sensitive information
+to reduce the likelihood of exposure.
+
+
+
+Restating our original question:
+_"How do I store secrets securely on the client?"_
+
+Our answer:
+_"Don't (but if you must, obfuscation wouldn't hurt)."_
+
+
+
+
diff --git a/2019-11-19-keyvaluepairs.md b/2019-11-19-keyvaluepairs.md
new file mode 100644
index 00000000..c770fe39
--- /dev/null
+++ b/2019-11-19-keyvaluepairs.md
@@ -0,0 +1,492 @@
+---
+title: KeyValuePairs
+author: Mattt
+category: Swift
+excerpt: >-
+ A look at an obscure, little collection type
+ that challenges our fundamental distinctions between
+ `Array`, `Set`, and `Dictionary`.
+status:
+ swift: 5.1
+---
+
+Cosmologies seek to create order
+by dividing existence into discrete, interdependent parts.
+Thinkers in every society throughout history
+have posited various arrangements ---
+though [Natural numbers](https://en.wikipedia.org/wiki/Natural_number)
+being what they are,
+there are only so many ways to slice the ontological pie.
+
+There are dichotomies like
+
+陰陽
+
+
+
+
+:
+incontrovertible and self-evident (albeit reductive).
+There are trinities,
+which position man in relation to heaven and earth.
+One might divide everything into four,
+[like the ancient Greeks](https://en.wikipedia.org/wiki/Classical_element)
+with the elements of
+earth, water, air, and fire.
+Or you could carve things into five,
+[like the Chinese](https://en.wikipedia.org/wiki/Wuxing_%28Chinese_philosophy%29)
+with
+
+
+
+木
+
+
+,
+
+
+
+火
+
+
+,
+
+
+
+土
+
+
+,
+
+
+
+金
+
+
+,
+and
+
+
+
+水
+
+
+.
+Still not satisfied?
+Perhaps the eight-part
+
+八卦
+
+
+will provide the answers that you seek:
+
+
+
+
+
Trigram
+
☰
+
☱
+
☲
+
☳
+
☴
+
☵
+
☶
+
☷
+
+
+
+
+
Nature
+
+
+ 天
+
+
+
+
+
+
+
+ 澤
+
+
+
+
+
+
+
+ 火
+
+
+
+
+
+
+
+ 雷
+
+
+
+
+
+
+
+ 風
+
+
+
+
+
+
+
+ 水
+
+
+
+
+
+
+
+ 山
+
+
+
+
+
+
+
+ 地
+
+
+
+
+
+
+
+
+
+Despite whatever [galaxy brain](/secrets/) opinion we may have about computer science,
+the pragmatic philosophy of day-to-day programming
+more closely aligns with a mundane cosmology;
+less imago universi, more
+
+じゃんけん
+
+
+.
+
+For a moment, ponder the mystical truths of fundamental Swift collection types:
+
+> Arrays are ordered collections of values. \\
+> Sets are unordered collections of unique values. \\
+> Dictionaries are unordered collections of key-value associations.
+> [The Book of Swift](https://docs.swift.org/swift-book/LanguageGuide/CollectionTypes.html)
+
+Thus compared to the pantheon of
+[`java.util` collections](https://docs.oracle.com/javase/7/docs/api/java/util/package-summary.html)
+or [`std` containers](http://www.cplusplus.com/reference/stl/),
+Swift offers a coherent coalition of three.
+Yet,
+just as we no longer explain everyday phenomena strictly in terms of
+[humors](https://en.wikipedia.org/wiki/Humorism#Four_humors)
+or [æther](https://en.wikipedia.org/wiki/Aether_%28classical_element%29),
+we must reject this formulation.
+Such a model is incomplete.
+
+We could stretch our understanding of sets to incorporate
+`OptionSet` (as explained in a [previous article](/optionset/)),
+but we'd be remiss to try and shoehorn `Range` and `ClosedRange`
+into the same bucket as `Array` ---
+and that's to say nothing of the panoply of
+[Swift Collection Protocols](/swift-collection-protocols)
+_(an article in dire need of revision)_.
+
+This week on NSHipster,
+we'll take a look at `KeyValuePairs`,
+a small collection type
+that challenges our fundamental distinctions between
+`Array`, `Set`, and `Dictionary`.
+In the process,
+we'll gain a new appreciation and a deeper understanding
+of the way things work in Swift.
+
+
+
+`KeyValuePairs` is a structure in the Swift standard library that ---
+_surprise, surprise_ ---
+represents a collection of key-value pairs.
+
+```swift
+struct KeyValuePairs: ExpressibleByDictionaryLiteral,
+ RandomAccessCollection
+{
+ typealias Element = (key: Key, value: Value)
+ typealias Index = Int
+ typealias Indices = Range
+ typealias SubSequence = Slice
+
+ <#...#>
+}
+```
+
+This truncated declaration highlights the defining features of `KeyValuePairs`:
+
+- Its ability to be expressed by a dictionary literal
+- Its capabilities as a random-access collection
+
+## KeyValuePairs as Expressible by Dictionary Literal
+
+[Literals](/swift-literals/)
+allow us to represent values directly in source code,
+and Swift is rather unique among other languages
+by extending this functionality to our own custom types through protocols.
+
+A dictionary literal
+represents a value as mapping of keys and values like so:
+
+```swift
+["key": "value"]
+```
+
+However, the term
+_"dictionary literal"_ is a slight misnomer,
+since a sequence of key-value pairs --- not a `Dictionary` ---
+are passed to the `ExpressibleByDictionaryLiteral` protocol's required initializer:
+
+```swift
+protocol ExpressibleByDictionaryLiteral {
+ associatedtype Key
+ associatedtype Value
+
+ init(dictionaryLiteral elements: (Key, Value)...)
+}
+```
+
+This confusion was amplified by the existence of a `DictionaryLiteral` type,
+which was only recently renamed to `KeyValuePairs` in Swift 5.
+The name change served to both clarify its true nature
+and bolster use as a public API
+(and not some internal language construct).
+
+You can create a `KeyValuePairs` object
+with a dictionary literal
+(in fact, this is the only way to create one):
+
+```swift
+let pairs: KeyValuePairs = [
+ "木": "wood",
+ "火": "fire",
+ "土": "ground",
+ "金": "metal",
+ "水": "water"
+]
+```
+
+{% info %}
+
+For more information about the history and rationale of this change,
+see
+[SE-0214: "Renaming the DictionaryLiteral type to KeyValuePairs"](https://github.com/apple/swift-evolution/blob/master/proposals/0214-DictionaryLiteral.md).
+
+{% endinfo %}
+
+## KeyValuePairs as Random-Access Collection
+
+`KeyValuePairs` conforms to `RandomAccessCollection`,
+which allows its contents to be retrieved by _(in this case, `Int`)_ indices.
+In contrast to `Array`,
+`KeyValuePairs` doesn't conform to `RangeReplaceableCollection`,
+so you can't append elements or remove individual elements at indices or ranges.
+This narrowly constrains `KeyValuePairs`,
+such that it's effectively immutable once initialized from a dictionary literal.
+
+These functional limitations are the key to understanding
+its narrow application in the standard library.
+
+## KeyValuePairs in the Wild
+
+Across the Swift standard library and Apple SDK,
+`KeyValuePairs` are found in just two places:
+
+- A [`Mirror` initializer](https://developer.apple.com/documentation/swift/mirror/3128579-init)
+ ([as discussed previously](/mirror/)):
+
+```swift
+struct Mirror {
+ init(_ subject: Subject,
+ children: KeyValuePairs,
+ displayStyle: DisplayStyle? = nil,
+ ancestorRepresentation: AncestorRepresentation = .generated)
+}
+
+typealias RGBA = UInt32
+typealias RGBAComponents = (UInt8, UInt8, UInt8, UInt8)
+
+let color: RGBA = 0xFFEFD5FF
+let mirror = Mirror(color,
+ children: ["name": "Papaya Whip",
+ "components": (0xFF, 0xEF, 0xD5, 0xFF) as RGBAComponents],
+ displayStyle: .struct)
+
+mirror.children.first(where: { (label, _) in label == "name" })?.value
+// "Papaya Whip"
+```
+
+- The [`@dynamicCallable` method](https://github.com/apple/swift-evolution/blob/master/proposals/0216-dynamic-callable.md):
+
+```swift
+@dynamicCallable
+struct KeywordCallable {
+ func dynamicallyCall(withKeywordArguments args: KeyValuePairs) -> Int {
+ return args.count
+ }
+}
+
+let object = KeywordCallable()
+object(a: 1, 2) // desugars to `object.dynamicallyCall(withKeywordArguments: ["a": 1, "": 2])`
+```
+
+On both occasions,
+`KeyValuePairs` is employed as an alternative to `[(Key, Value)]`
+to enforce restraint by the caller.
+Without any other public initializers,
+`KeyValuePairs` can only be constructed from dictionary literals,
+and can't be constructed dynamically.
+
+
+
+## Working with KeyValuePairs Values
+
+If you want to do any kind of work with a `KeyValuePairs`,
+you'll first want to convert it into a conventional `Collection` type ---
+either `Array` or `Dictionary`.
+
+### Converting to Arrays
+
+`KeyValuePairs` is a `Sequence`,
+by virtue of its conformance to `RandomAccessCollection`
+(and therefore `Collection`).
+When we pass it to the corresponding `Array` initializer,
+it becomes an array of its associated `Element` type (`(Key, Value)`).
+
+```swift
+let arrayOfPairs: [(Key, Value)] = Array(pairs)
+```
+
+Though,
+if you just want to iterate over each key-value pair,
+its conformance to `Sequence` means that you can pass it directly to a `for-in` loop:
+
+```swift
+for (key, value) in pairs {
+ <#...#>
+}
+```
+
+You can always create an `Array` from a `KeyValuePairs` object,
+but creating a `Dictionary` is more complicated.
+
+## Converting to Dictionaries
+
+There are four built-in types that conform to `ExpressibleByDictionaryLiteral`:
+
+- `Dictionary`
+- `NSDictionary`
+- `NSMutableDictionary`
+- `KeyValuePairs`
+
+Each of the three dictionary types constitutes a
+[surjective mapping](https://en.wikipedia.org/wiki/Surjective_function),
+such that every value element has one or more corresponding keys.
+`KeyValuePairs` is the odd one out:
+it instead maintains an ordered list of tuples
+that allows for duplicate key associations.
+
+`Dictionary` got a number of convenient initializers in Swift 4
+thanks to [SE-0165](https://github.com/apple/swift-evolution/blob/master/proposals/0165-dict.md)
+_(thanks, [Nate](/authors/nate-cook/)!)_,
+including
+`init(uniqueKeysWithValues:)`,
+`init(_:uniquingKeysWith:)`, and
+`init(grouping:by)`
+
+Consider the following example that
+constructs a `KeyValuePairs` object with a duplicate key:
+
+```swift
+let pairsWithDuplicateKey: KeyValuePairs = [
+ "天": "Heaven",
+ "澤": "Lake",
+ "澤": "Marsh",
+ <#...#>
+]
+```
+
+Attempting to pass this to `init(uniqueKeysWithValues:)`
+results in a fatal error:
+
+```swift
+Dictionary(uniqueKeysWithValues: Array(pairsWithDuplicateKey))
+// Fatal error: Duplicate values for key: '澤'
+```
+
+Instead, you must either specify which value to map
+or map each key to an array of values:
+
+```swift
+Dictionary(Array(pairsWithDuplicateKey),
+ uniquingKeysWith: { (first, _) in first })
+// ["澤": "Lake", <#...#>]
+
+Dictionary(Array(pairsWithDuplicateKey),
+ uniquingKeysWith: { (_, last) in last })
+// ["澤": "Marsh", <#...#>]
+
+Dictionary(grouping: Array(pairsWithDuplicateKey),
+ by: { (pair) in pair.value })
+// ["澤": ["Lake", "Marsh"], <#...#>]
+```
+
+
+
+
+
+Outside of its narrow application in the standard library,
+`KeyValuePairs` are unlikely to make an appearance in your own codebase.
+You're almost always better off going with a simple `[(Key, Value)]` tuple array.
+
+Much as today's [Standard Model](https://en.wikipedia.org/wiki/Standard_Model)
+more closely resembles
+the cacophony of a
+[zoo](https://en.wikipedia.org/wiki/Particle_zoo)
+than the
+musica universalis of
+[celestial spheres](https://en.wikipedia.org/wiki/Celestial_spheres),
+`KeyValuePairs`
+challenges our tripartite view of Swift collection types.
+But like all cosmological exceptions ---
+though uncomfortable or even unwelcome at times ---
+it serves to expand our understanding.
+
+That's indeed its key value.
+
+{% asset "articles/keyvaluepairs.css" %}
diff --git a/2019-11-25-bless.md b/2019-11-25-bless.md
new file mode 100644
index 00000000..48dcf415
--- /dev/null
+++ b/2019-11-25-bless.md
@@ -0,0 +1,320 @@
+---
+title: bless
+author: Mattt
+category: Miscellaneous
+excerpt: >-
+ The process of booting a computer is a small miracle.
+ Starting with a blank slate,
+ a computer incrementally runs smaller, simpler programs
+ to load larger, more capable programs into memory,
+ until it finally has everything it needs to run the operating system itself.
+---
+
+This Thursday is [Thanksgiving][american thanksgiving] in the United States,
+a holiday in which we'll gather together to cultivate and express gratitude
+against a backdrop of turkey, mashed potatoes, and
+origin-myths of European colonialism in the Americas.
+
+{% info %}
+
+[Canadian Thanksgiving][canadian thanksgiving]
+is celebrated on the second Monday in October ---
+coincidentally, the day Americans observe
+Columbus Day
+[Indigenous Peoples' Day][indigenous peoples day].
+
+
+
+{% endinfo %}
+
+As the year ---
+_and indeed the decade_ ---
+draws to a close,
+we're invited to reflect on the blessings in our lives:
+The individuals who fill them with joy,
+the circumstances that bring them comfort and security,
+the pursuits that endow them with meaning.
+
+...of course, it's also a time of ritualized, over-indulgent consumption.
+And for the PC gamers among us,
+that means shutting down our Macs,
+booting into our [Windows][boot camp] partitions,
+and settling into our long-neglected back catalogs on Steam.
+
+
+
+So while you wait for your AAA titles to download
+and your Windows software updates to apply
+_(let's assume you're reading this on your iPhone)_,
+we invite you to count your blessings.
+
+
+
+This week on NSHipster:
+a quick look at the `bless` command and the macOS boot process.
+
+But first, a quick disclaimer:
+
+{% warning %}
+
+If you want to boot into your Windows partition,
+skip the command-line entirely.
+Instead, open the Startup Disk preference pane,
+select `BOOTCAMP`,
+and click the Restart button.
+
+
+
+
+
+
+
+
+If you search the Web for
+_"how to set Boot Camp as startup disk from Terminal"_,
+you'll find plenty of old blog posts that mention `bless`.
+However, for reasons that we'll discuss later on,
+this won't work on the latest Mac hardware and software.
+
+{% endwarning %}
+
+
+
+The process of booting a computer is a small miracle.
+Starting with nothing ---
+"tabula rasa" ---
+a computer incrementally runs smaller, simpler programs
+to load larger, more capable programs into memory,
+until it finally has everything it needs to
+run the operating system itself.
+
+In a previous era of computers,
+operating systems were initialized using
+[BIOS][bios] firmware,
+which came pre-installed in system
+ROM.
+When a machine powered up,
+that firmware would be the first thing that the computer encountered,
+and it'd proceed to load machine code from the [boot sector][boot sector].
+
+Nowadays,
+Macs and most other computers boot according to the
+[Extensible Firmware Interface][efi]
+(EFI),
+which introduces a few levels of indirection for greater flexibility.
+When a machine powers on under this regime,
+it loads a boot manager,
+which is responsible for loading configuration from
+non-volatile RAM
+(NVRAM),
+including the file system paths of the
+loaders and kernels for the operating system.
+This additional step allows computers to
+boot into different partitions on disk
+and even network volumes.
+
+{% info %}
+
+For the curious,
+you can see what variables are currently set in NVRAM
+by running the following command:
+
+```terminal
+$ nvram -x -p |
+ xmllint --xpath "//key" - |
+ sed -E "s#([^<]+)#\1,#g" |
+ tr "," "\n"
+HW_BOOT_DATA
+IONVRAM-SYNCNOW-PROPERTY
+LocationServicesEnabled
+SleepWakeFailureString
+<#...#>
+```
+
+{% endinfo %}
+
+The process of making a volume bootable is called blessing,
+and the operative command to accomplish this has been, historically,
+[`bless`][bless].
+
+{% info %}
+
+Long before Boot Camp,
+in the early days of Mac OS X,
+one of the common uses of the `bless` command
+was booting into your ["Classic"][classic mac os] Mac OS 9 partition:
+
+```terminal
+# Boot into Mac OS 9
+sudo bless -folder9 '/Volumes/Mac OS 9/System Folder' -setOF
+
+# Boot into Mac OS X
+sudo bless -folder '/System/Library/CoreServices' -setOF
+```
+
+{% endinfo %}
+
+To get an overview of the blessed volumes on your machine,
+run `bless --info`:
+
+
+
+```terminal
+$ sudo bless --info
+<#...#> => Blessed System File is {Preboot}/<#...#>/System/Library/CoreServices/boot.efi
+<#...#> => Blessed System Folder is {Preboot}/<#...#>/System/Library/CoreServices
+The blessed volume in this APFS container is "/".
+No blessed APFS snapshot for this volume.
+```
+
+{% error %}
+
+Unless you know what you're doing,
+that's pretty much the extent to which you should be invoking the `bless` command.
+Other usage can cause your Mac to become unbootable,
+so exercise caution.
+
+{% enderror %}
+
+Blessing a volume on the latest Mac hardware running the latest version of macOS
+is an involved process,
+which includes
+(among other things):
+
+- Copying `boot.efi` to the correct file system path on the volume
+- Writing information to the volume header to designate it as being bootable
+- Setting the `efi-boot-device` and `efi-boot-device-data` variables
+ to designate the volume as the next startup disk
+
+To illustrate how much complexity is baked into blessing a volume,
+let's focus on the first of these tasks:
+
+> Copy `boot.efi`? Easy: `cp boot.efi "/Volumes/Macintosh HD"`. \\
+> (It's a UNIX system! I know this!)
+
+_Well, not quite._
+
+On a Mac booted from an APFS disk,
+`boot.efi` is in a special preboot volume (`/dev/disk1s2`),
+at a path containing a generated UUID.
+HFS+ is rather simpler by comparison,
+since the boot loader has a hard-coded located at
+`/System/Library/CoreServices/boot.efi`.
+That is, unless,
+the disk is encrypted with [FileVault full-disk encryption][filevault]
+or is part of a RAID array that requires additional drivers.
+And all of this is to say nothing of [Secure Boot][secure boot]
+and the [Apple T2 Security Chip][t2],
+which comes standard on the iMac Pro as well as
+Mac Mini, MacBook Air, and MacBook Pro computers since 2018.
+
+But in case you remain undeterred,
+and _really_ want to avoid the inconvenience of opening up System Preferences,
+here's the invocation you seek:
+
+```terminal
+$ sudo bless -mount /Volumes/BOOTCAMP -setBoot --nextonly
+Could not set boot device property: 0xe00002e2
+```
+
+However,
+that hasn't worked since the advent of
+[System Integrity Protection][sip]
+(SIP)
+in macOS 10.11.
+
+A suggested workaround invokes `systemsetup`
+as a replacement for `bless`.
+
+The [`systemsetup`][systemsetup] command is similar to System Preferences,
+in that it can be used to set machine-wide preferences,
+including:
+date and time settings,
+remote login and Apple Events and
+the behavior of the physical power button,
+as well as when a computer should go to sleep and
+what should happen after a power failure.
+
+The `systemsetup` invocation to set the startup disk
+goes a little something like this:
+
+```terminal
+$ sudo systemsetup -setstartupdisk /Volume/BOOTCAMP/Windows
+Setting startup disk not allowed while System Integrity Protection is enabled.
+```
+
+But that, too,
+doesn't seem to work on recent versions of macOS
+without disabling SIP
+_(and for the vast majority of folks,
+there's never a good reason to do this)_.
+
+
+
+
+
+So by now,
+Windows has finished updating itself _(maybe)_,
+the game is 100% downloaded and ready to launch,
+and you're left wondering
+_"What's the point of this article?"_
+
+To that, dear reader,
+I'd first say that there are far, _far_ worse things than being pointless
+_(or [Point-Free](https://www.pointfree.co), as it were)_.
+But if you're still searching for a takeaway,
+here it is:
+
+> You probably won't ever have an occasion to use the `bless` command
+> _(or `systemsetup`, for that matter)_.
+
+With every design decision,
+software vendors perform a balancing act between security and convenience.
+For most people,
+the new security features in recent versions of macOS and new Apple hardware
+make them undeniably safer,
+usually without any perceivable downside.
+Yet we, as developers,
+especially those of us with a hacker mentality,
+may see the Mac's trajectory towards a closed, iOS-like security model
+as a mixed blessing ---
+a curse, even.
+
+If that's the case for you
+_(and even if it isn't)_,
+then please allow us to offer another helping of article-concluding takeaway:
+
+> Take time to be thankful for all that you have,
+> and seek to be present for those around you this holiday season.
+
+[apfs]: https://developer.apple.com/documentation/foundation/file_system/about_apple_file_system
+[sip]: https://support.apple.com/en-us/HT204899
+[boot camp]: https://support.apple.com/boot-camp
+[t2]: https://support.apple.com/en-us/HT208862
+[secure boot]: https://support.apple.com/en-us/HT208330
+[indigenous peoples day]: https://en.wikipedia.org/wiki/Indigenous_Peoples%27_Day
+[uniform monday holiday act]: https://en.wikipedia.org/wiki/Uniform_Monday_Holiday_Act
+[bless]: x-man-page://8/bless
+[systemsetup]: x-man-page://8/systemsetup
+[efi]: https://en.wikipedia.org/wiki/Unified_Extensible_Firmware_Interface#Booting
+[nvram]: https://en.wikipedia.org/wiki/Non-volatile_random-access_memory
+[boot sector]: https://en.wikipedia.org/wiki/Boot_sector
+[bios]: https://en.wikipedia.org/wiki/BIOS
+[american thanksgiving]: https://en.wikipedia.org/wiki/Thanksgiving_%28United_States%29
+[canadian thanksgiving]: https://en.wikipedia.org/wiki/Thanksgiving_%28Canada%29
+[bootstrap]: https://en.wikipedia.org/wiki/Bootstrapping#Etymology
+[classic mac os]: https://en.wikipedia.org/wiki/Classic_Mac_OS
+[filevault]: https://support.apple.com/en-us/HT204837
diff --git a/2019-12-10-available.md b/2019-12-10-available.md
new file mode 100644
index 00000000..7970fae3
--- /dev/null
+++ b/2019-12-10-available.md
@@ -0,0 +1,799 @@
+---
+title: Swift API Availability
+author: Mattt
+category: Swift
+excerpt: >-
+ Code exists in a world of infinite abundance.
+ Whatever you can imagine is willed into being...
+ for the most part.
+ Because despite the boundless potential afforded to us,
+ we often find ourselves constrained by circumstances beyond us.
+status:
+ swift: 5.1
+---
+
+Code exists in a world of infinite abundance.
+Whatever you can imagine is willed into being ---
+so long as you know how to express your desires.
+
+As developers,
+we know that code will eventually be compiled into software,
+and forced to compete in the real-world for
+allocation of scarce hardware resources.
+Though up until that point,
+we can luxuriate in the feeling of unbounded idealism...
+_well, mostly_.
+For software is not a pure science,
+and our job --- in reality ---
+is little more than
+shuttling data through broken pipes between leaky abstractions.
+
+This week on NSHipster,
+we're exploring a quintessential aspect of our unglamorous job:
+API availability.
+The good news is that Swift provides first-class constructs
+for dealing with these real-world constraints
+by way of `@available` and `#available`.
+However,
+there are a few nuances to these language features,
+of which many Swift developers are unaware.
+So be sure to read on
+to make sure that you're clear on all the options available to you.
+
+---
+
+## @available
+
+In Swift,
+you use the `@available` attribute
+to annotate APIs with availability information,
+such as
+"this API is deprecated in macOS 10.15" or
+"this API requires Swift 5.1 or higher".
+With this information,
+the compiler can ensure that any such APIs used by your app
+are available to all platforms supported by the current target.
+
+The `@available` attribute can be applied to declarations,
+including
+top-level functions, constants, and variables,
+types like structures, classes, enumerations, and protocols,
+and type members ---
+initializers, class deinitializers, methods, properties, and subscripts.
+
+{% info %}
+
+The `@available` attribute, however, can't be applied to
+[operator precedence group](/swift-operators/) (`precedencegroup`) or
+protocol associated type (`associatedtype`)
+declarations.
+
+{% endinfo %}
+
+### Platform Availability
+
+When used to designate platform availability for an API,
+the `@available` attribute can take one or two forms:
+
+- A "shorthand specification"
+ that lists minimum version requirements for multiple platforms
+- An extended specification
+ that can communicate additional details about
+ availability for a single platform
+
+#### Shorthand Specification
+
+```swift
+@available(<#platform#> <#version#> <#, [platform version] ...#>, *)
+```
+
+- A platform;
+ `iOS`,
+ `macCatalyst`,
+ `macOS` / `OSX`,
+ `tvOS`, or
+ `watchOS`,
+ or any of those with `ApplicationExtension` appended
+ _(e.g. `macOSApplicationExtension`)_.
+- A version number
+ consisting of one, two, or three positive integers,
+ separated by a period (`.`),
+ to denote the [major, minor, and patch version](https://semver.org).
+- Zero or more versioned platforms in a comma-delimited (`,`) list.
+- An asterisk (`*`),
+ denoting that the API is available for all other platforms.
+ An asterisk is always required for platform availability annotations
+ to handle potential future platforms
+ (_such as the [long-rumored `iDishwasherOS`](https://github.com/apple/swift/commit/0d7996f4ee1ce9b7f9f1ea1d2e3ad71394d91eb1#diff-f142ec4252ddcbeea5be368189f43481R25)_).
+
+For example,
+new, cross-platform APIs introduced at [WWDC 2019](/wwdc-2019/)
+might be annotated as:
+
+```swift
+@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
+```
+
+{% info %}
+
+The list of available platform names is
+[specified by the Clang compiler front-end](https://github.com/llvm-mirror/clang/blob/master/include/clang/Basic/Attr.td#L782-L789).
+
+Look a few lines up, though, and you'll find a curious mention of
+["android"](https://github.com/llvm-mirror/clang/blob/master/include/clang/Basic/Attr.td#L756)(introduced by [D7929](https://reviews.llvm.org/D7929)).
+_Does anyone have more context about why that's in there?_
+
+{% endinfo %}
+
+Shorthand specifications are a convenient way
+to annotate platform availability.
+But if you need to communicate more information,
+such as when or why an API was deprecated
+or what should be used as a replacement,
+you'll want to opt for an extended specification instead.
+
+### Introduced, Deprecated, Obsoleted, and Unavailable
+
+```swift
+// With introduced, deprecated, and/or obsoleted
+@available(<#platform | *#>
+ <#, introduced: version#> <#, deprecated: version#> <#, obsoleted: version#>
+ <#[, renamed: "..."]#>
+ <#[, message: "..."]#>)
+
+// With unavailable
+@available(<#platform | *#>, unavailable <#[, renamed: "..."]#> <#[, message: "..."]#>)
+```
+
+- A platform, same as before,
+ or an asterisk (\*) for all platforms.
+- Either `introduced`, `deprecated`, and/or `obsoleted`...
+ - An `introduced` version,
+ denoting the first version in which the API is available
+ - A `deprecated` version,
+ denoting the first version when using the API
+ generates a compiler warning
+ - An `obsoleted` version,
+ denoting the first version when using the API
+ generates a compiler error
+- ...or `unavailable`,
+ which causes the API to generate a compiler error when used
+- `renamed` with a keypath to another API;
+ when provided, Xcode provides an automatic "fix-it"
+- A `message` string to be included in the compiler warning or error
+
+Unlike shorthand specifications,
+this form allows for only one platform to be specified.
+So if you want to annotate availability for multiple platforms,
+you'll need stack `@available` attributes.
+For example,
+here's how the previous shorthand example
+can be expressed in multiple attributes:
+
+```swift
+@available(macOS, introduced: 10.15)
+@available(iOS, introduced: 13)
+@available(watchOS, introduced: 6)
+@available(tvOS, introduced: 13)
+```
+
+{% info %}
+
+Once again,
+diving into the underlying Clang source code
+unearths some curious tidbits.
+In addition to the `@available` arguments listed above,
+[Clang includes `strict` and `priority`](https://github.com/llvm-mirror/clang/blob/master/include/clang/Basic/Attr.td#L748-L752).
+Neither of these works in Swift,
+but it's interesting nonetheless.
+
+{% endinfo %}
+
+
+
+Apple SDK frameworks make extensive use of availability annotations
+to designate new and deprecated APIs
+with each release of iOS, macOS, and other platforms.
+
+For example,
+iOS 13 introduces a new
+[`UICollectionViewCompositionalLayout`](https://developer.apple.com/documentation/uikit/uicollectionviewcompositionallayout) class.
+If you jump to its declaration
+by holding command (⌘) and clicking on that symbol in Xcode,
+you'll see that it's annotated with `@available`:
+
+```swift
+@available(iOS 13.0, *)
+open class UICollectionViewCompositionalLayout : UICollectionViewLayout { <#...#> }
+```
+
+
+
+This `@available` attribute tells the compiler that
+`UICollectionViewCompositionalLayout` can only be called
+on devices running iOS, version 13.0 or later
+_(with caveats; see note below)_.
+
+If your app targets iOS 13 only,
+then you can use `UICollectionViewCompositionalLayout`
+without any special consideration.
+If, however,
+your deployment target is set below iOS 13
+in order to support previous versions of iOS
+(as is the case for many existing apps),
+then any use of `UICollectionViewCompositionalLayout`
+must be conditionalized.
+
+More on that in a moment.
+
+{% info %}
+
+When an API is marked as available in iOS,
+it's implicitly marked available on tvOS and Mac Catalyst,
+because both of those platforms are derivatives of iOS.
+That's why, for example,
+the (missing) documentation for `UICollectionViewCompositionalLayout`
+reports availability in
+iOS 13.0+,
+Mac Catalyst 13.0+, and
+tvOS 13.0+
+despite its declaration only mentions `iOS 13.0`.
+
+If necessary,
+you can explicitly mark these derived platforms as being unavailable
+with additional `@available` attributes:
+
+```swift
+@available(iOS 13, *)
+@available(tvOS, unavailable)
+@available(macCatalyst, unavailable)
+func handleShakeGesture() { <#...#> }
+```
+
+{% endinfo %}
+
+### Swift Language Availability
+
+Your code may depend on a new language feature of Swift,
+such as [property wrappers](/propertywrapper/)
+or [default enumeration case associated values](https://github.com/apple/swift-evolution/blob/master/proposals/0155-normalize-enum-case-representation.md#default-parameter-values-for-enum-constructors) ---
+both new in Swift 5.1.
+If you want to support development of your app with previous versions of Xcode
+or for your Swift package to work for multiple Swift compiler toolchains,
+you can use the `@available` attribute
+to annotate declarations containing new language features.
+
+When used to designate Swift language availability for an API,
+the `@available` attribute takes the following form:
+
+```swift
+@available(swift <#version#>)
+```
+
+Unlike platform availability,
+Swift language version annotations
+don't require an asterisk (`*`);
+to the compiler, there's one Swift language,
+with multiple versions.
+
+{% info %}
+
+Historically,
+the `@available` attribute was first introduced for platform availability
+and later expanded to include Swift language version availability with
+[SE-0141](https://github.com/apple/swift-evolution/blob/master/proposals/0141-available-by-swift-version.md).
+Although these use cases share a syntax,
+we recommend that you keep any combined application
+in separate attributes for greater clarity.
+
+```swift
+import Foundation
+
+@available(swift 5.1)
+@available(iOS 13.0, macOS 10.15, *)
+@propertyWrapper
+struct WebSocketed {
+ private var value: Value
+ var wrappedValue: URLSessionWebSocketTask.Message {
+ get { .string(value) }
+ set {
+ if case let .string(description) = newValue {
+ value = Value(description)
+ }
+ }
+ }
+}
+```
+
+{% endinfo %}
+
+## #available
+
+In Swift,
+you can predicate `if`, `guard`, and `while` statements
+with an availability condition,
+`#available`,
+to determine the availability of APIs at runtime.
+Unlike the `@available` attribute,
+an `#available` condition can't be used for Swift language version checks.
+
+The syntax of an `#available` expression
+resembles that of an `@available` attribute:
+
+```swift
+<#if | guard | while#> #available(<#platform#> <#version#> <#, [platform version] ...#>, *) <#...#>
+```
+
+{% info %}
+
+`#available` expressions in Swift
+have the same syntax as their
+[Objective-C counterpart](/at-compiler-directives/#availability), `@available`.
+
+{% endinfo %}
+
+{% warning %}
+
+You can’t combine multiple `#available` expressions
+using logical operators like `&&` and `||`,
+but you can use commas,
+which are equivalent to `&&`.
+In practice,
+this is only useful for conditioning
+Swift language version and the availability of a single platform
+_(since a check for more than one would be either redundant or impossible)_.
+
+```swift
+// Require Swift 5 and iOS 13
+guard #available(swift 5.0), #available(iOS 13.0) else { return }
+```
+
+{% endwarning %}
+
+---
+
+Now that we know how APIs are annotated for availability,
+let's look at how to annotate and conditionalize code
+based on our platform and/or Swift language version.
+
+---
+
+## Working with Unavailable APIs
+
+Similar to how, in Swift,
+[thrown errors must be handled or propagated](https://docs.swift.org/swift-book/LanguageGuide/ErrorHandling.html#ID512),
+use of potentially unavailable APIs
+must be either annotated or conditionalized code.
+
+When you attempt to call an API that is unavailable
+for at least one of your supported targets,
+Xcode will recommend the following options:
+
+- "Add `if #available` version check"
+- "Add `@available` attribute to enclosing declaration"
+ _(suggested at each enclosing scope; for example,
+ the current method as well as that method's containing class)_
+
+Following our analogy to error handling,
+the first option is similar to prepending `try` to a function call,
+and the second option is akin to wrapping a statement within `do/catch`.
+
+For example,
+within an app supporting iOS 12 and iOS 13,
+a class that subclasses `UICollectionViewCompositionalLayout`
+must have its declaration annotated with `@available`,
+and any references to that subclass
+would need to be conditionalized with `#available`:
+
+```swift
+@available(iOS 13.0, *)
+final class CustomCompositionalLayout: UICollectionViewCompositionalLayout { <#...#> }
+
+func createLayout() -> UICollectionViewLayout {
+ if #available(iOS 13, *) {
+ return CustomCompositionalLayout()
+ } else {
+ return UICollectionViewFlowLayout()
+ }
+}
+```
+
+Swift comprises many inter-dependent concepts,
+and crosscutting concerns like availability
+often result in significant complexity
+as various parts of the language interact.
+For instance,
+what happens if you create a subclass that overrides
+a property marked as unavailable by its superclass?
+Or what if you try to call a function that's renamed on one platform,
+but replaced by an operator on another?
+
+While it'd be tedious to enumerate every specific behavior here,
+these questions can and often do arise
+in the messy business of developing apps in the real world.
+If you find yourself wondering "what if"
+and tire of trial-and-error experimentation,
+you might instead try consulting the
+[Swift language test suite](https://github.com/apple/swift/blob/6663800cdc5e4d0d4d10c767a0f2f7fc426cfa1f/test/attr/attr_availability.swift)
+to determine what's expected behavior.
+
+{% info %}
+
+A quick shout-out to [Slava Pestov](https://github.com/slavapestov)
+for [this gem of a test case](https://github.com/apple/swift/commit/ae6c75ab5d438a4fe23a4d944d70e6143e3f38de):
+
+```swift
+@available(OSX, introduced: 10.53)
+class EsotericSmallBatchHipsterThing : WidelyAvailableBase {}
+```
+
+{% endinfo %}
+
+Alternatively,
+you can surmise how things work generally
+from [Clang's diagnostic text](https://clang.llvm.org/docs/DiagnosticsReference.html#wavailability):
+
+{::nomarkdown}
+
+
+-Wavailability Diagnostic Text
+
+
+
+-Wavailability Diagnostic Text
+
+
+
+
warning:
+
+ ‘unavailable’ availability overrides all other availability information
+
+
+
+
warning:
+
+ unknown platform
+ A
+ in availability macro
+
+
+
+
warning:
+
+ feature cannot be
+
+
+
+
+
+
+ introduced
+
+
+
+
+ deprecated
+
+
+
+
+ obsoleted
+
+
+
+
+
+
+ in
+ B
+ version
+ C
+ before it was
+
+
+
+
+
+
+ introduced
+
+
+
+
+ deprecated
+
+
+
+
+ obsoleted
+
+
+
+
+
+
+ in version
+ E
+ ; attribute ignored
+
+
+
+
warning:
+
+ use same version number separators ‘_’ or ‘.’; as in ‘major[.minor[.subminor]]’
+
+
+
+
warning:
+
+ availability does not match previous declaration
+
+
+
+
warning:
+
+
+
+
+
+
+
+
+
+
+ overriding
+
+
+
+
+
+
+ method
+
+
+
+
+
+
+ introduced after
+
+
+
+
+ deprecated before
+
+
+
+
+ obsoleted before
+
+
+
+
+
+
+
+
+
+
+ the protocol method it implements
+
+
+
+
+ overridden method
+
+
+
+
+
+
+ on
+ B
+ (
+ C
+ vs.
+ D
+ )
+
+
+
+
warning:
+
+
+
+
+
+
+
+
+
+
+ overriding
+
+
+
+
+
+
+ method cannot be unavailable onAwhen
+
+
+
+
+
+ the protocol method it implements
+
+
+
+
+ its overridden method
+
+
+
+
+
+
is available
+
+
+
+
+
+
+
+
+{:/}
+
+## Annotating Availability in Your Own APIs
+
+Although you'll frequently interact with `@available`
+as a consumer of Apple APIs,
+you're much less likely to use them as an API producer.
+
+### Availability in Apps
+
+Within the context of an app,
+it may be convenient to use `@available` deprecation warnings
+to communicate across a team
+that use of a view controller, convenience method, or what have you
+is no longer advisable.
+
+```swift
+@available(iOS, deprecated: 13, renamed: "NewAndImprovedViewController")
+class OldViewController: UIViewController { <#...#> }
+
+class NewAndImprovedViewController: UIViewController { <#...#> }
+```
+
+Use of `unavailable` or `deprecated`, however,
+are much less useful for apps;
+without any expectation to vend an API outside that context,
+you can simply remove an API outright.
+
+### Availability in Third-Party Frameworks
+
+If you maintain a framework that depends on the Apple SDK in some way,
+you may need to annotate your APIs according
+to the availability of the underlying system calls.
+For example,
+a convenience wrapper around
+[Keychain APIs](https://developer.apple.com/documentation/security/keychain_services)
+would likely annotate the availability of
+platform-specific biometric features like Touch ID and Face ID.
+
+However,
+if your framework wraps SDK functionality
+in a way that doesn't expose such implementation details,
+you might be able to opt-in to new features
+without affecting the availability for your own APIs.
+For example,
+an NLP
+library that delegates functionality to
+[`NSLinguisticTagger`](/nslinguistictagger/)
+could instead use
+[Natural Language framework](https://developer.apple.com/documentation/naturallanguage/)
+when available
+(as determined by `#available`),
+without any user-visible API changes.
+
+### Availability in Swift Packages
+
+If you're writing Swift qua Swift
+in a platform-agnostic way
+and distributing that code as a Swift package,
+you may want to use `@available`
+to give a heads-up to consumers about APIs that are on the way out.
+
+Unfortunately,
+there's currently no way to designate deprecation
+in terms of the library version
+(the list of platforms are hard-coded by the compiler).
+While it's a bit of a hack,
+you could communicate deprecation
+by specifying an obsolete / non-existent Swift language version
+like so:
+
+```swift
+@available(swift, deprecated: 0.0.1, message: "Deprecated in 1.2.0")
+func going(going: Gone...) {}
+```
+
+{% info %}
+
+The closest we have to package versioning
+is the [private `_PackageDescription` platform](https://forums.swift.org/t/leveraging-availability-for-packagedescription-apis/18667)
+used by the Swift Package Manager.
+
+```swift
+public enum SwiftVersion {
+ @available(_PackageDescription, introduced: 4, obsoleted: 5)
+ case v3
+
+ @available(_PackageDescription, introduced: 4)
+ case v4
+
+ <#...#>
+}
+```
+
+If you're interested in extending this functionality
+to third-party packages,
+[consider starting a discussion in Swift Evolution](https://forums.swift.org/t/library-version-as-available-attribute/30019/2).
+
+{% endinfo %}
+
+## Working Around Deprecation Warnings
+
+As some of us are keenly aware,
+[it's not currently possible to silence deprecation warnings in Swift](https://forums.swift.org/t/swift-should-allow-for-suppression-of-warnings-especially-those-that-come-from-objective-c/19216).
+Whereas in Objective-C,
+you could suppress warnings with `#pragma clang diagnostic push / ignored / pop`,
+no such convenience is afforded to Swift.
+
+If you're among the l33t coders who have "hard mode" turned on
+("Treat Warnings as Errors" a.k.a. `SWIFT_TREAT_WARNINGS_AS_ERRORS`),
+but find yourself stymied by deprecation warnings,
+here's a cheat code you can use:
+
+```swift
+class CustomView {
+ @available(iOS, introduced: 10, deprecated: 13, message: "😪")
+ func method() {}
+}
+
+CustomView().method() // "'method()' was deprecated in iOS 13: 😪"
+
+protocol IgnoringMethodDeprecation {
+ func method()
+}
+
+extension CustomView: IgnoringMethodDeprecation {}
+
+(CustomView() as IgnoringMethodDeprecation).method() // No warning! 😁
+```
+
+---
+
+As an occupation,
+programming epitomizes the post-scarcity ideal
+of a post-industrial world economy
+in the information age.
+But even so far removed from physical limitations,
+we remain inherently constrained by forces beyond our control.
+However,
+with careful and deliberate practice,
+we can learn to make use of everything available to us.
+
+{% asset "articles/availability.css" %}
diff --git a/2019-12-16-direct.md b/2019-12-16-direct.md
new file mode 100644
index 00000000..18a7f0a2
--- /dev/null
+++ b/2019-12-16-direct.md
@@ -0,0 +1,412 @@
+---
+title: Objective-C Direct Methods
+author: Mattt
+category: Objective-C
+excerpt: >-
+ It's hard to get excited when new features come to Objective-C.
+ These days,
+ any such improvements are in service of Swift interoperability
+ rather than an investment in the language itself.
+ So it was surprising to learn about
+ a recent patch to Clang.
+---
+
+It's hard to get excited when new features come to Objective-C.
+These days,
+any such improvements are in service of Swift interoperability
+rather than an investment in the language itself
+_(see [nullability](https://developer.apple.com/swift/blog/?id=25)
+and [lightweight generics](https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/using_imported_lightweight_generics_in_swift))_.
+
+So it was surprising to learn about
+[this recently merged patch to Clang](https://reviews.llvm.org/D69991),
+which adds a new direct dispatch mechanism to Objective-C methods.
+
+The genesis of this new language feature is unclear;
+the most we have to go on is an Apple-internal [Radar number](/bug-reporting/)
+([`2684889`](rdar://2684889)),
+which doesn't tell us much beyond its relative age
+(sometime in the early '00s, by our estimation).
+Fortunately,
+[the feature landed](https://github.com/llvm/llvm-project/commit/d4e1ba3fa9dfec2613bdcc7db0b58dea490c56b1)
+with enough documentation and test coverage
+to get a good idea of how it works.
+_(Kudos to implementor Pierre Habouzit,
+review manager John McCall,
+and the other LLVM contributors)_.
+
+This week on NSHipster,
+we're taking this occasion to review Objective-C method dispatching
+and try to understand the potential impact of this new language feature
+on future codebases.
+
+
+
+---
+
+To understand the significance of direct methods,
+you need to know a few things about the Objective-C runtime.
+But let's start our discussion one step before that,
+to the origin of OOP itself:
+
+## Object-Oriented Programming
+
+Alan Kay coined the term "object-oriented programming in the late 1960s.
+With the help of Adele Goldberg, Dan Ingalls, and his other colleagues at
+Xerox PARC,
+Kay put this idea into practice in the '70s
+with the creation of the Smalltalk programming language.
+
+
+
+In the 1980s,
+Brad Cox and Tom Love started work the first version of Objective-C,
+a language that sought to take the object-oriented paradigm of Smalltalk
+and implement it on solid fundamentals of C.
+Through a series of fortuitous events in the '90s,
+the language would come to be the official language of NeXT,
+and later, Apple.
+
+For those of us who started learning Objective-C in the iPhone era,
+the language was often seen as yet another proprietary Apple technology ---
+one of a myriad, obscure byproducts of the company's
+["Not invented here"](https://en.wikipedia.org/wiki/Not_invented_here)
+(NIH) culture.
+However,
+Objective-C isn't just "an object-oriented C",
+it's one of _the original_ object-oriented languages,
+with as strong a claim to OOP credentials as any other.
+
+Now, what does OOP mean?
+That's a good question.
+'90s era hype cycles have rendered the term almost meaningless.
+However,
+for our purposes today,
+let's focus on something Alan Kay wrote in 1998:
+
+> I'm sorry that I long ago coined the
+> term "objects" for this topic because it gets many people to
+> focus on the lesser idea.
+> The big idea is "messaging" [...] > Alan Kay
+
+## Dynamic Dispatch and the Objective-C Runtime
+
+In Objective-C,
+a program consists of a collection of objects
+that interact with each other by passing messages that, in turn,
+invoke methods, or functions.
+This act of message passing is denoted by square bracket syntax:
+
+```objc
+[someObject aMethod:withAnArgument];
+```
+
+When Objective-C code is compiled,
+message sends are transformed into calls to a function called
+[`objc_msgSend`](https://developer.apple.com/documentation/objectivec/1456712-objc_msgsend)
+(literally _"send a message to some object with an argument"_).
+
+```objc
+objc_msgSend(object, @selector(message), withAnArgument);
+```
+
+- The first argument is the receiver (`self` for instance methods)
+- The second argument is `_cmd`: the selector, or name of the method
+- Any method parameters are passed as additional function arguments
+
+`objc_msgSend` is responsible for determining
+which underlying implementation to call in response to this message,
+a process known as method dispatch.
+
+In Objective-C,
+each class (`Class`) maintains a dispatch table to resolve messages sent at runtime.
+Each entry in the dispatch table is a method (`Method`)
+that keys a selector (`SEL`)
+to a corresponding implementation (`IMP`),
+which is a pointer to a C function.
+When an object receives a message,
+it consults the dispatch table of its class.
+If it can find an implementation for the selector,
+the associated function is called.
+Otherwise,
+the object consults the dispatch table of its superclass.
+This continues up the inheritance chain until a match is found
+or the root class (`NSObject`) deems the selector to be unrecognized.
+
+
+
+If you think all of this indirection sounds like a lot of work...
+in a way,
+you'd be right!
+
+If you have a hot path in your code,
+an expensive method that's called frequently,
+you could imagine some benefit to avoiding all of this indirection.
+To that end,
+some developers have used C functions as a way around dynamic dispatch.
+
+## Direct Dispatch with a C Function
+
+As we saw with `objc_msgSend`,
+any method invocation can be represented by an equivalent function
+by passing implicit `self` as the first argument.
+
+For example,
+consider the following declaration of an Objective-C class
+with a conventional, dynamically-dispatched method.
+
+```objc
+@interface MyClass: NSObject
+- (void)dynamicMethod;
+@end
+```
+
+If a developer wanted to implement some functionality on `MyClass`
+without going through the whole message sending shebang,
+they could declare a static C function
+that took an instance of `MyClass` as an argument.
+
+```objc
+static void directFunction(MyClass *__unsafe_unretained object);
+```
+
+Here's how each of these approaches translates to the call site:
+
+```objc
+MyClass *object = [[[MyClass] alloc] init];
+
+// Dynamic Dispatch
+[object dynamicMethod];
+
+// Direct Dispatch
+directFunction(object);
+```
+
+## Direct Methods
+
+A direct method has the look and feel of a conventional method,
+but has the behavior of a C function.
+When a direct method is called,
+it directly calls its underlying implementation
+rather than going through `objc_msgSend`.
+
+With this new LLVM patch,
+you now have a way to annotate Objective-C methods
+to avoid participation in dynamic dispatch selectively.
+
+### objc_direct, @property(direct), and objc_direct_members
+
+To make an instance or class method direct,
+you can mark it with the `objc_direct`
+[Clang attribute](/__attribute__/).
+Likewise,
+the methods for an Objective-C property can be made direct
+by declaring it with the `direct` property attribute.
+
+```objc
+@interface MyClass: NSObject
+@property(nonatomic) BOOL dynamicProperty;
+@property(nonatomic, direct) BOOL directProperty;
+
+- (void)dynamicMethod;
+- (void)directMethod __attribute__((objc_direct));
+@end
+```
+
+{% info %}
+
+By our count,
+the addition of `direct` brings the total number of `@property` attributes to 16:
+
+- `getter` and
+ `setter`
+- `readwrite` and
+ `readonly`,
+- `atomic` and
+ `nonatomic`
+- `weak`,
+ `strong`,
+ `copy`,
+ `retain`, and
+ `unsafe_unretained`
+- `nullable`,
+ `nonnullable`, and
+ `null_resettable`
+- `class`
+
+{% endinfo %}
+
+When an `@interface` for
+a category or class extension is annotated with the `objc_direct_members` attribute,
+all method and property declarations contained within it
+are considered to be direct,
+unless previously declared by that class.
+
+{% warning %}
+
+You can't annotate the primary class interface
+with the `objc_direct_members` attribute.
+
+{% endwarning %}
+
+```objc
+__attribute__((objc_direct_members))
+@interface MyClass ()
+@property (nonatomic) BOOL directExtensionProperty;
+- (void)directExtensionMethod;
+@end
+```
+
+Annotating an `@implementation` with `objc_direct_members` has a similar effect,
+causing non-previously declared members to be deemed direct,
+including any implicit methods resulting from property synthesis.
+
+```objc
+__attribute__((objc_direct_members))
+@implementation MyClass
+- (BOOL)directProperty {<#...#>}
+- (void)dynamicMethod {<#...#>}
+- (void)directMethod {<#...#>}
+- (void)directExtensionMethod {<#...#>}
+- (void)directImplementationMethod {<#...#>}
+@end
+```
+
+{% error %}
+
+A dynamic method can't be overridden in a subclass by a direct method,
+and a direct method can't be overridden at all.
+
+Protocols can't declare direct method requirements,
+and a class can't implement a protocol requirement
+with a direct method.
+
+{% enderror %}
+
+Applying these annotations to our example from before,
+we can see how direct and dynamic methods are indistinguishable at the call site:
+
+```objc
+MyClass *object = [[[MyClass] alloc] init];
+
+// Dynamic Dispatch
+[object dynamicMethod];
+
+// Direct Dispatch
+[object directMethod];
+```
+
+---
+
+Direct methods seem like a slam dunk feature
+for the performance-minded developers among us.
+But here's the twist:
+
+**In most cases,
+making a method direct probably won't have a noticeable performance advantage.**
+
+As it turns out,
+[`objc_msgSend` is surprisingly fast](https://www.mikeash.com/pyblog/friday-qa-2016-04-15-performance-comparisons-of-common-operations-2016-edition.html).
+Thanks to aggressive caching, extensive low-level optimization,
+and intrinsic performance characteristics of modern processors,
+`objc_msgSend` has an extremely low overhead.
+
+We're long past the days when iPhone hardware
+could reasonably be described as a resource-constrained environment.
+So unless Apple is preparing for a new embedded platform
+_([AR glasses, anyone?](http://appleinsider.com/articles/17/01/09/rumor-apple-working-with-carl-zeiss-on-ar-glasses-to-debut-in-2018))_,
+the most reasonable explanation we have for
+Apple implementing Objective-C direct methods in 2019
+stems from something other than performance.
+
+{% info %}
+
+[Mike Ash](https://www.mikeash.com)
+is the Internet's foremost expert on `objc_msgSend`.
+Over the years,
+his posts have provided the deepest and most complete understanding
+to the Objective-C runtime you'll find outside of Cupertino.
+For the curious,
+["Dissecting objc_msgSend on ARM64"](https://www.mikeash.com/pyblog/friday-qa-2017-06-30-dissecting-objc_msgsend-on-arm64.html)
+is a great place to start.
+
+{% endinfo %}
+
+## Hidden Motives
+
+When an Objective-C method is marked as direct,
+its implementation has hidden visibility.
+That is,
+direct methods can only be called within the same module
+_(or to be pedantic,
+[linkage unit](https://clang.llvm.org/docs/LTOVisibility.html))._
+It won't even show up in the Objective-C runtime.
+
+Hidden visibility has two direct advantages:
+
+- Smaller binary size
+- No external invocation
+
+Without external visibility
+or a way to invoke them dynamically from the Objective-C runtime,
+direct methods are effectively private methods.
+
+{% info %}
+
+If you want to participate in direct dispatch,
+but still want to make your API accessible externally,
+you can wrap it in a C function.
+
+```objc
+static inline void performDirectMethod(MyClass *__unsafe_unretained object) {
+ [object directMethod];
+}
+```
+
+{% endinfo %}
+
+While hidden visibility can be used by Apple
+to prevent swizzling and private API use,
+that doesn't seem to be the primary motivation.
+
+[According to Pierre](https://twitter.com/pedantcoder/status/1197269246289444864),
+who implemented this feature,
+the main benefit of this optimization is code size reduction.
+Reportedly,
+the weight of unused Objective-C metadata
+can account for 5 – 10% of the `__text` section in the compiled binary.
+
+---
+
+You could imagine that,
+from now until next year's developer conference,
+a few engineers could go through each of the SDK frameworks,
+annotating private methods with `objc_direct`
+and private classes with `objc_direct_members`
+as a lightweight way to progressively tighten its SDK.
+
+If that's true,
+then perhaps it's just as well that we've become skeptical of new Objective-C features.
+When they're not in service of Swift,
+they're in service of Apple.
+Despite its important place in the history of programming and Apple itself,
+it's hard not to see Objective-C as just that --- _history_.
diff --git a/2020-01-01-escape-velocity.md b/2020-01-01-escape-velocity.md
new file mode 100644
index 00000000..5a7baff9
--- /dev/null
+++ b/2020-01-01-escape-velocity.md
@@ -0,0 +1,278 @@
+---
+title: Escape Velocity
+author: Mattt
+category: Miscellaneous
+published: false
+excerpt: >-
+ Swift is great for apps and not much else right now.
+---
+
+As someone born between 1981 and 1996,
+I fall into the widely accepted definition of a "Millenial",
+the generation so named for coming of age around the year 2000.
+
+I remember staying up to watch the ball drop in 1999,
+tuned into MTV's _New Year's Eve Live_.
+As the East Coast collectively counted down the seconds to midnight,
+the broadcast from Times Square had
+Gwen Stefani performing a cover of R.E.M.'s
+[_"It's the End of the World As We Know It."_](https://music.apple.com/us/album/its-the-end-of-the-world-as-we-know-it-and-i-feel-fine/1440850529?i=1440850619),
+in an _ironic-maybe-not-ironic_ nod to the very real fears
+many of us had about Y2K at the time.
+
+Every year since,
+I've taken New Year's Eve as an occasion for solemn contemplation.
+Being another mod 10 year
+and following the year that was 2019,
+I can't help thinking a lot about the end of the world
+and wondering how I feel about it.
+This week, I'd like to invite you, dear reader,
+to crack open a can of [Surge][surge],
+queue up some
+[Robbie Williams](https://music.apple.com/gb/album/millennium/1440864572?i=1440864990)
+on your Sony Walkman™,
+and hear me out as I talk through some things.
+
+
+
+* * *
+
+Swift was announced on June 2nd, 2014,
+and (if the official repository is to be believed)
+development started on
+[July 17, 2010](https://github.com/apple/swift/commit/18844bc65229786b96b89a9fc7739c0fc897905e).
+
+Apple has a proven track record of destructive innovation,
+of _"out with the old"_,
+of _"get on board or get out of the way"_.
+So when the announcement came,
+each of us had a choice to make:
+Either full-throated support or irrelevance.
+Many of us at the time were all too eager to jump in head-first
+and discounted criticism as a form of apostacy, of heresy, of being a buzz-kill.
+For anyone who missed this the first time around,
+you can see pretty much the same dynamic playing out now with SwiftUI.
+
+Every decision has an opportunity cost,
+and I know a lot of people who came to regret going all in so early,
+myself included.
+Even when I was actually being paid by Apple to make Swift better,
+I struggled to justify the trajectory of my career so far.
+I knew I wanted to get out and do something else,
+but I'd dug myself in deep and needed to build up enough speed to get out.
+
+* * *
+
+When Swift was open-sourced at the end of 2015,
+[the Swift.org website](https://web.archive.org/web/20151203210715/https://swift.org/)
+included the following as part of its welcome message:
+
+> Now that Swift is open source,
+> you can help make the best general purpose programming language available everywhere.
+> Swift.org, archived December 3, 2015 by The Internet Archive
+
+Five years after its public release
+and a decade since it first started,
+it's hard to deny that Swift has struggled to gain widespread adoption
+outside of the Apple ecosystem.
+And current trends don't provide much assurance that this will ever change.
+
+
+
+In
+[Stack Overflow's Developer Survey for 2019](https://insights.stackoverflow.com/survey/2019#most-popular-technologies),
+Swift ranks 14th under "Most Popular Technologies"
+among respondents who described themselves as professional developers.
+
+On
+[GitHub's ranking of Top languages over time](https://octoverse.github.com/#top-languages-over-time)
+Swift isn't ranked among the most popular language on GitHub
+by repository contributors
+_(though Swift's influence can be felt through
+Objective-C's disappearance after 2015)._
+
+Swift did manage to snag the #10 spot in the latest
+[TIOBE Index](https://www.tiobe.com/tiobe-index/) update.
+However, this achievement is undercut by the fact that
+TIOBE twice named Objective-C its "Programming Language of the Year"
+(in 2010 and 2011),
+which peaked at position #3 in March 2015.
+
+{% info %}
+
+If this seems unfair,
+consider that Swift is roughly as old as
+[Go][go] (2009),
+[Rust][rust] (2010),
+[Kotlin][kotlin] (2011),
+[TypeScript][typescript] (2012), and
+[Elixir][elixir] (2012),
+each of which have arguably seen greater adoption than Swift
+for systems and web application development.
+
+{% endinfo %}
+
+* * *
+
+If Swift and Xcode is all you know,
+it's hard to appreciate how far behind it is to other languages
+in terms of tooling.
+If I had to describe the experience of,
+for example,
+writing [React](https://reactjs.org)
+in [TypeScript](https://www.typescriptlang.org)
+with [Visual Studio Code](/vscode/)
+and [React Developer Tools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en):
+Imagine [SwiftUI Previews](/swiftui-previews/),
+but how you'd expect it to work in Xcode 16.
+
+It's a prime example of
+the [Cathedral and Bazaar models](https://en.wikipedia.org/wiki/The_Cathedral_and_the_Bazaar)
+of software development.
+And that same disparity can be found in terms of package ecosystem, too.
+
+Apple provides a lot of functionality for its platforms
+_([more than they can document, in fact!](https://nooverviewavailable.com/))_,
+and for everything else,
+the iOS developer community has done an admirable job to fill the gaps.
+But try to use Swift for anything other than an iDevice,
+and you'll quickly find yourself having to reinvent functionality
+far removed from the problem you _actually_ want to solve.
+
+Last year,
+[npm](https://npmjs.com), the largest registry for Node.js packages,
+[crossed the 1 million mark](https://snyk.io/blog/npm-passes-the-1-millionth-package-milestone-what-can-we-learn/).
+Some other points of comparison:
+[Maven Centeral](https://search.maven.org) (Java),
+[nuget](https://www.nuget.org/packages) (.NET),
+[CPAN](https://www.cpan.org) (Perl)
+[PyPI](https://pypistats.org) (Python), and
+[RubyGems](https://rubygems.org/stats) (Ruby),
+each have a total package count in the low 6 figures.
+
+How many Swift packages are there?
+It's hard to know for sure,
+but it's a lot fewer than any of those other languages.
+
+{% info %}
+
+The closest thing Swift has to a mainstream, centralized package registry is
+[CocoaPods](/cocoapods/),
+which reports over 69K _(nice)_ entries.
+But that number includes both Objective-C and Swift libraries,
+and it's unclear what the breakdown is.
+
+We can use GitHub search to establish some general parameters
+for what the real count is.
+As a lower bounds,
+searching GitHub for projects with a `Package.swift` files in their root directory
+[returns around twenty thousand results](https://github.com/search?utf8=✓&q=path%3A%2F+filename%3APackage.swift&type=Code&ref=advsearch&l=&l=).
+As an upper bounds,
+searching all Swift projects with more than 1★ (excluding forks),
+[the count is closer to fifty thousand](https://github.com/search?l=&p=99&q=stars%3A%3E3+language%3ASwift&ref=advsearch&type=Repositories&utf8=✓)
+
+{% endinfo %}
+
+Granted,
+numbers aren't everything,
+and massive `node_modules` directories
+exist in their own special circle of
+["Dependency Hell"](https://en.wikipedia.org/wiki/Dependency_hell).
+But,
+as the saying goes,
+Quantity has a quality all its own.
+Even if you write off 90% of JavaScript packages as garbage,
+you're still more than _twice_ as likely
+to find an existing solution to a problem
+from an npm module than any Swift package.
+It's hard to overstate the difference of
+reliably having prior art for most programming tasks.
+
+For the time being,
+any competitive advantage Swift might claim outside of app development,
+whether in terms of performance or client / server code reuse,
+is overshadowed by the weakness of its ecosystem.
+
+Swift is great for apps and not much else right now.\\
+_But then again,
+what good are apps, anyway?_
+
+* * *
+
+Take a moment to think about the most important problems you're facing right now
+as an individual.
+Next, think about the biggest problems facing society.
+Now ask yourself:
+_How many of those problems can be solved by apps?_
+
+
+
+I don't mean to sound glib,
+but the problems most worth solving today
+are political and social in nature, not technological ---
+and to the extent that technological solutions exist,
+they're much more likely to manifest in the form of a website or database
+_(or heck, even an Excel spreadsheet)_
+than an app.
+
+Every era may believe themselves to exist on a knife's edge,
+at the precipice of total annihilation.
+But it's not unreasonable to see the unique peril of our current age.
+
+
+
+When recruiting John Sculley, then CEO of PepsiCo,
+to become the new CEO of Apple in the early 1980's,
+Steve Jobs famously asked him:
+
+> Do you want to spend the rest of your life selling sugared water,
+> or do you want a chance to change the world?
+> Steve Jobs
+
+To that end, ask yourself: \\
+_Do you really want to spend your life writing apps?_
+
+* * *
+
+Thinking about the smartest developers I knew around the last `mod 10` changeover ---
+the folks who were there with me during that iOS renaissance in the early '10s ---
+I can’t help but notice that very few of them are still "making apps".
+
+Some of them transitioned into management tracks,
+while a few managed to build successful businesses.
+I know several people who shifted their focus to tooling
+in support of their growing team of engineers.
+The coolest among them started making videogames.
+Still others jumped ship to the .NET stack
+and seem all the happier for it.
+
+So what comes next?
+_I don't know._
+Do you?
+
+[surge]: https://en.wikipedia.org/wiki/Surge_(drink)
+[go]: https://en.wikipedia.org/wiki/Go_(programming_language)
+[rust]: https://en.wikipedia.org/wiki/Rust_(programming_language)
+[kotlin]: https://en.wikipedia.org/wiki/Kotlin_(programming_language)
+[typescript]: https://en.wikipedia.org/wiki/TypeScript
+[elixir]: https://en.wikipedia.org/wiki/Elixir_(programming_language)
diff --git a/2020-01-06-at-compiler-directives.md b/2020-01-06-at-compiler-directives.md
new file mode 100644
index 00000000..5f7a780e
--- /dev/null
+++ b/2020-01-06-at-compiler-directives.md
@@ -0,0 +1,503 @@
+---
+title: "@"
+author: Mattt
+category: Objective-C
+tags: nshipster
+excerpt: >-
+ If we were to go code-watching for Objective-C,
+ what would we look for?
+ Square brackets,
+ ridiculously long method names,
+ and `@`'s.
+revisions:
+ 2020-01-06: Updated for Xcode 11
+---
+
+Birdwatchers refer to it as
+_(and I swear I'm not making this up)_
+["Jizz"](https://en.wikipedia.org/wiki/Jizz_%28birding%29):
+the general characteristics that form an overall impression of a thing.
+
+Walking through the forests of the Pacific Northwest,
+a birder would know a nighthawk from other little brown jobs
+from its distinct vocalization,
+or a grey-cheeked thrush by its white-dark-white underwing pattern.
+Looking up in the sky,
+there'd be no mistaking a Flying-V formation of migratory geese
+from the undulating murmuration of starlings.
+And while a twitcher would be forgiven for
+mistaking a coot for a duck at the water's edge,
+their scaley, non-webbed feet are an obvious tell to an ornithophile.
+
+The usefulness of jizz isn't limited to amateur ornithology, either.
+We can distinguish varieties of programming languages
+based on their defining characteristics:
+Go with its tell-tale couplets of `if err`,
+Rust with its unpronounceable, consonant-laden keywords, `pub`, `fn`, and `mut`,
+Perl with its special characters that read like Q\*bert swearing.
+Lisp's profusion of parentheses is an old cliché at this point;
+our favorite telling is
+[that one joke](https://discuss.fogcreek.com/joelonsoftware3/default.asp?cmd=show&ixPost=94232&ixReplies=38)
+about the stolen last page of a Lisp program's printed source code.
+
+```lisp
+ )))
+ ) )
+ ))) ) ))
+ )))))
+ )))
+ ))
+ )))) ))
+ )))) ))
+ )))
+)
+```
+
+* * *
+
+If we were to go code-watching for the elusive Objective-C species,
+what would we look for?
+Square brackets,
+ridiculously long method names,
+and `@`'s.
+
+`@`, or "at" sign compiler directives,
+are as central to understanding Objective-C's gestalt
+as its ancestry and underlying mechanisms.
+Those little cinnamon roll glyphs are the sugary glue
+that allows Objective-C to be such a powerful, expressive language,
+and yet still compile down to C.
+So varied and disparate are its uses that
+the only accurate way to describe what `@` means on its own is
+_"shorthand for something to do with Objective-C"_.
+They cover a broad range in usefulness and obscurity,
+from staples like `@interface` and `@implementation`
+to ones you could go your whole career without spotting,
+like `@defs` and `@compatibility_alias`.
+But to anyone aspiring to be an NSHipster,
+knowledge of every `@` directives
+is tantamount to a birder's familiarity with
+the frenetic hovering of a hummingbird,
+the commanding top knot of a Mountain quail, or
+the eponymous "cuckoo" of _Coccyzus americanus_.
+
+## Interface & Implementation
+
+`@interface` and `@implementation` are the first things you encounter
+when you start learning Objective-C:
+
+```objc
+// MyObject.h
+@interface MyObject
+<#...#>
+@end
+
+// MyObject.m
+@implementation MyObject
+<#...#>
+@end
+```
+
+What you don't learn about until later on are categories and class extensions.
+
+Categories allow you to extend the behavior of existing classes
+by adding new class or instance methods.
+As a convention,
+categories are defined in their own `.{h,m}` files:
+
+```objc
+// MyObject+CategoryName.h
+@interface MyObject (CategoryName)
+ - (void)foo;
+ - (BOOL)barWithBaz:(NSInteger)baz;
+@end
+
+// MyObject+CategoryName.m
+@implementation MyObject (CategoryName)
+ - (void)foo {
+ <#...#>
+ }
+
+ - (BOOL)barWithBaz:(NSInteger)baz {
+ return YES;
+ }
+@end
+```
+
+Categories are particularly useful for convenience methods on standard framework classes
+_(just don't go overboard with your utility functions)_.
+
+Extensions look like categories
+but omit the category name.
+They're typically declared before an `@implementation`
+to specify a private interface
+or override properties declared in the public interface:
+
+```objc
+// MyObject.m
+@interface MyObject ()
+@property (readwrite, nonatomic, strong) NSString *name;
+- (void)somePrivateMethod;
+@end
+
+@implementation MyObject
+<#...#>
+@end
+```
+
+### Properties
+
+Property directives are likewise,
+learned early on:
+
+`@property`
+: Declares a class or instance property.
+
+`@synthesize`
+: Automatically synthesizes getter / setter methods
+ to an underlying instance or class variable
+ for a declared property.
+
+`@dynamic`
+: Instructs the compiler that you'll provide your own
+ implementation for property getter and/or setter methods.
+
+{% info %}
+
+All `@property` declarations are now automatically synthesized by default
+(since Xcode 4.4),
+so you're much less likely to find `@synthesize` in Objective-C code bases these days.
+
+{% endinfo %}
+
+### Property Attributes
+
+
+`@property` declarations comprise their own little sub-phylum of syntax,
+with attributes for specifying:
+
+- Accessor names
+ (`getter` / `setter`)
+- Access types
+ (`readwrite` / `readonly`)
+- [Atomicity](https://en.wikipedia.org/wiki/Linearizability)
+ (`atomic` / `nonatomic`)
+- [Nullability](https://clang.llvm.org/docs/analyzer/developer-docs/nullability.html)
+ (`nullable` / `nonnullable` / `null_resettable`)
+- [Ownership](https://clang.llvm.org/docs/AutomaticReferenceCounting.html#ownership-qualification)
+ (`weak` / `strong` / `copy` / `retain` / `assign` / `unsafe_unretained`)
+
+And that's not all ---
+there's also the `class` attribute,
+which lets you declare a class property using
+the same, familiar instance property syntax,
+as well as [the forthcoming `direct` attribute](/direct/),
+which will let you opt in to direct method dispatch.
+
+### Forward Class Declarations
+
+Occasionally,
+`@interface` declarations will reference an external type in a property or as a parameter.
+Rather than adding an `#import` statement in the interface,
+you can use a forward class declaration in the header
+and import the necessary in the implementation.
+
+`@class`
+: Creates a forward declaration,
+ allowing a class to be referenced before its actual declaration.
+
+Shorter compile times,
+less chance of cyclical references;
+you should get in the habit of doing this if you aren't already.
+
+### Instance Variable Visibility
+
+It's a matter of general convention that
+classes provide state and mutating interfaces through properties and methods,
+rather than directly exposing ivars.
+Nonetheless,
+in cases where ivars _are_ directly manipulated,
+there are the following visibility directives:
+
+`@public`
+: Instance variable can be read and written to directly
+ using the following notation:
+
+```objc
+object->_ivar = <#...#>
+```
+
+`@package`
+: Instance variable is public,
+ except outside of the framework in which it is specified
+ (64-bit architectures only)
+
+`@protected`
+: Instance variable is only accessible to its class and derived classes
+
+`@private`
+: Instance variable is only accessible to its class
+
+```objc
+@interface Person : NSObject {
+ @public
+ NSString *name;
+ int age;
+
+ @private
+ int salary;
+}
+@end
+```
+
+## Protocols
+
+There's a distinct point early in an Objective-C programmer's evolution
+when they realize that they can define their own protocols.
+
+The beauty of protocols is that they let you design contracts
+that can be adopted outside of a class hierarchy.
+It's the egalitarian mantra at the heart of the American Dream:
+It doesn't matter who you are or where you come from;
+anyone can achieve anything if they work hard enough.
+
+`@protocol`...`@end`
+defines a set of methods to be implemented by any conforming class,
+as if they were added to the interface of that class directly.
+
+Architectural stability and expressiveness without the burden of coupling?
+Protocols are awesome.
+
+### Requirement Options
+
+You can further tailor a protocol by specifying methods as required or optional.
+Optional methods are stubbed in the interface,
+so as to be auto-completed in Xcode,
+but do not generate a warning if the method isn't implemented.
+Protocol methods are required by default.
+
+The syntax for `@required` and `@optional` follows that of the visibility macros:
+
+```objc
+@protocol CustomControlDelegate
+ - (void)control:(CustomControl *)control didSucceedWithResult:(id)result;
+@optional
+ - (void)control:(CustomControl *)control didFailWithError:(NSError *)error;
+@end
+```
+
+## Exception Handling
+
+Objective-C communicates unexpected state primarily through `NSError`.
+Whereas other languages would use exception handling for this,
+Objective-C relegates exceptions to truly exceptional behavior.
+
+`@` directives are used for the traditional convention of `try/catch/finally` blocks:
+
+```objc
+@try{
+ // attempt to execute the following statements
+ [self getValue:&value error:&error];
+
+ // if an exception is raised, or explicitly thrown...
+ if (error) {
+ @throw exception;
+ }
+} @catch(NSException *e) {
+ <#...#>
+} @finally {
+ // always executed after @try or @catch
+ [self cleanup];
+}
+```
+
+## Literals
+
+Literals are shorthand notation for specifying fixed values,
+and their availability in a language
+is directly correlated with programmer happiness.
+By that measure,
+Objective-C has long been a language of programmer misery.
+
+### Object Literals
+
+For years,
+Objective-C only had literals for `NSString` values.
+But with the release of the
+[Apple LLVM 4.0 compiler](http://clang.llvm.org/docs/ObjectiveCLiterals.html),
+there are now literals for `NSNumber`, `NSArray`, and `NSDictionary`.
+
+`@""`
+: An `NSString` object initialized with
+ the text inside the quotation marks.
+
+`@42` / `@3.14` / `@YES` / `@'Z'`
+: An `NSNumber` object initialized with
+ the adjacent value using the pertinent class constructor,
+ such that
+ `@42` → `[NSNumber numberWithInteger:42]` and
+ `@YES` → `[NSNumber numberWithBool:YES]`.
+ _(You can use suffixes to further specify type,
+ like `@42U` → `[NSNumber numberWithUnsignedInt:42U]`)_
+
+`@[]`
+: An `NSArray` object initialized with
+ a comma-delimited list of objects as its contents.
+ It uses the `+arrayWithObjects:count:` class constructor method,
+ which is a more precise alternative to the more familiar
+ `+arrayWithObjects:`.
+
+`@{}`
+: An `NSDictionary` object initialized with key-value pairs as its contents
+ using the format: `@{@"someKey" : @"theValue"}`.
+
+`@()`
+: A boxed expression using the appropriate object literal for the enclosed value
+ _(for example, `NSString` for `const char*`,
+ `NSNumber` for `int`, and so on)_.
+ This is also the designated way to use number literals with `enum` values.
+
+### Objective-C Literals
+
+Selectors and protocols can be passed as method parameters.
+`@selector()` and `@protocol()` serve as pseudo-literal directives
+that return a pointer to a particular selector (`SEL`) or protocol (`Protocol *`).
+
+`@selector()`
+: Provides an `SEL` pointer to a selector with the specified name.
+ Used in methods like `-performSelector:withObject:`.
+
+`@protocol()`
+: Provides a `Protocol *` pointer to the protocol with the specified name.
+ Used in methods like `-conformsToProtocol:`.
+
+### C Literals
+
+Literals can also work the other way around,
+transforming Objective-C objects into C values.
+These directives in particular allow us to peek underneath the Objective-C veil
+to see what's really going on.
+
+Did you know that all Objective-C classes and objects are just glorified `struct`s?
+Or that the entire identity of an object hinges on a single `isa` field in that `struct`?
+
+For most of us,
+most of the time,
+this is an academic exercise.
+But for anyone venturing into low-level optimizations,
+this is simply the jumping-off point.
+
+`@encode()`
+: Provides the [type encoding](/type-encodings/) of a type.
+ This value can be used as the first argument in
+ `NSCoder -encodeValueOfObjCType:at:`.
+
+`@defs()`
+: Provides the layout of an Objective-C class.
+ For example,
+ `struct { @defs(NSObject) }`
+ declares a struct with the same fields as an `NSObject`:
+
+{% warning %}
+`@defs` is unavailable in the modern Objective-C runtime.
+{% endwarning %}
+
+## Optimizations
+
+Some `@` compiler directives provide shortcuts for common optimizations.
+
+`@autoreleasepool {<#...#>}`
+: If your code contains a tight loop that creates lots of temporary objects,
+ you can use the `@autoreleasepool` directive to
+ aggressively deallocate these short-lived, locally-scoped objects.
+ `@autoreleasepool` replaces and improves upon the old `NSAutoreleasePool`,
+ which was significantly slower and unavailable with ARC.
+
+`@synchronized(<#object#>) {<#...#>}`
+: Guarantees the safe execution of a particular block within a specified context
+ (usually `self`).
+ Locking in this way is expensive, however,
+ so for classes aiming for a particular level of thread safety,
+ a dedicated `NSLock` property
+ or the use of low-level primitives like GCD
+ are preferred.
+
+## Compatibility
+
+When Apple introduces a new API,
+it's typically available for the latest SDK only.
+If you want to start using these APIs in your app
+without dropping backward compatibility,
+you can create a compatibility alias.
+
+For example,
+back when [UICollectionView](/uicollectionview/) was first introduced in iOS 6,
+many developers incorporated a 3rd-party library called
+[PSTCollectionView](https://github.com/steipete/PSTCollectionView),
+which uses `@compatibility_alias` to provide a backwards-compatible,
+drop-in replacement for `UICollectionView`:
+
+```objc
+#if __IPHONE_OS_VERSION_MAX_ALLOWED < 60000
+@compatibility_alias UICollectionViewController PSTCollectionViewController;
+@compatibility_alias UICollectionView PSTCollectionView;
+@compatibility_alias UICollectionReusableView PSTCollectionReusableView;
+@compatibility_alias UICollectionViewCell PSTCollectionViewCell;
+@compatibility_alias UICollectionViewLayout PSTCollectionViewLayout;
+@compatibility_alias UICollectionViewFlowLayout PSTCollectionViewFlowLayout;
+@compatibility_alias UICollectionViewLayoutAttributes PSTCollectionViewLayoutAttributes;
+@protocol UICollectionViewDataSource @end
+@protocol UICollectionViewDelegate @end
+#endif
+```
+
+You can use the same approach today to strategically adopt new APIs in your app,
+alongside the next and final `@` compiler directive in this week's article:
+
+## Availability
+
+Achieving backwards or cross-platform compatibility in your app
+can often feel like a high-wire act.
+If you so much as glance towards an unavailable class or method,
+it could mean curtains for your app.
+That's why the new features in Clang 5.0 came as such a relief.
+Now developers have a compiler-provide safety net
+to warn them whenever an unavailable API is referenced
+for one of your supported targets.
+
+`@available`
+: Use in an `if` statement to have the compiler
+ conditionally execute a code path based on the platform availability.
+
+
+For example,
+if you wanted to use a `fancyNewMethod` in the latest version of macOS,
+but provide a fallback for older versions of macOS:
+
+```objc
+- (void)performCalculation {
+ if (@available(macOS 10.15, *)) {
+ [self fancyNewMethod];
+ } else {
+ [self oldReliableMethod];
+ }
+}
+```
+
+{% info %}
+
+`@available` expressions in Objective-C
+have the same syntax as their [Swift counterpart](/available/), `#available`.
+
+{% endinfo %}
+
+---
+
+Much like the familiar call of a warbler
+or the tell-tale plumage of a peacock,
+the `@` sigil plays a central role
+in establishing Objective-C's unique identity.
+It's a versatile, power-packed character
+that embodies the underlying design and mechanisms of the language.
+So be on the lookout for its many faces
+as you wander through codebases, new or familiar.
diff --git a/2020-01-29-rawrepresentable.md b/2020-01-29-rawrepresentable.md
new file mode 100644
index 00000000..87c72abf
--- /dev/null
+++ b/2020-01-29-rawrepresentable.md
@@ -0,0 +1,397 @@
+---
+title: RawRepresentable
+author: Mattt
+category: Swift
+excerpt: >-
+ Programming is about typing.
+ And programming languages are typically judged by how much they make you type —
+ in both senses of the word.
+status:
+ swift: 5.1
+---
+
+Programming is about typing.
+And programming languages are typically judged by how much they make you type ---
+in both senses of the word.
+
+Swift is beloved for being able to save us a few keystrokes
+without compromising safety or performance,
+whether it's through
+implicit typing or
+automatic synthesis of protocols like
+[`Equatable`](/equatable-and-comparable/) and
+[`Hashable`](/hashable/).
+But the OG
+ergonomic feature of Swift is undoubtedly
+automatic synthesis of `RawRepresentable` conformance
+for enumerations with raw types.
+You know...
+the language feature that lets you do this:
+
+```swift
+enum Greeting: String {
+ case hello = "hello"
+ case goodbye // implicit raw value of "goodbye"
+}
+
+enum SortOrder: Int {
+ case ascending = -1
+ case same // implicit raw value of 0
+ case descending // implicit raw value of 1
+}
+```
+
+Though _"enum + RawValue"_ has been carved into the oak tree of our hearts
+since first we laid eyes on that language with a fast bird,
+few of us have had occasion to consider
+what `RawRepresentable` means outside of autosynthesis.
+This week,
+we invite you to do a little extra typing
+and explore some untypical use cases for the `RawRepresentable` protocol.
+
+--------------------------------------------------------------------------------
+
+In Swift,
+an enumeration can be declared with
+raw value syntax.
+
+According to [the documentation](https://developer.apple.com/documentation/swift/rawrepresentable):
+
+> For any enumeration with a string, integer, or floating-point raw type,
+> the Swift compiler automatically adds `RawRepresentable` conformance.
+
+When developers first start working with Swift,
+they inevitably run into situations where raw value syntax doesn't work:
+
+- Enumerations with raw values other than `Int` or `String`
+- Enumerations with associated values
+
+Upon seeing those bright, red error sigils,
+many of us fall back to a more conventional enumeration,
+failing to realize that what we wanted to do wasn't impossible,
+but rather just slightly beyond what the compiler can do for us.
+
+--------------------------------------------------------------------------------
+
+## RawRepresentable with C Raw Value Types
+
+The primary motivation for raw value enumerations is
+to improve interoperability.
+Quoting again from the docs:
+
+> Using the raw value of a conforming type
+> streamlines interoperation with Objective-C and legacy APIs.
+
+This is true of Objective-C frameworks in the Apple SDK,
+which declare enumerations with [`NS_ENUM`](/ns_enum-ns_options/).
+But interoperability with other C libraries is often less seamless.
+
+Consider the task of interfacing with
+[libcmark](https://github.com/commonmark/cmark),
+a library for working with Markdown according to the
+[CommonMark spec](http://spec.commonmark.org/).
+Among the imported data types is `cmark_node_type`,
+which has the following C declaration:
+
+```c
+typedef enum {
+ /* Error status */
+ CMARK_NODE_NONE,
+
+ /* Block */
+ CMARK_NODE_DOCUMENT,
+ CMARK_NODE_BLOCK_QUOTE,
+ <#...#>
+ CMARK_NODE_HEADING,
+ CMARK_NODE_THEMATIC_BREAK,
+
+ CMARK_NODE_FIRST_BLOCK = CMARK_NODE_DOCUMENT,
+ CMARK_NODE_LAST_BLOCK = CMARK_NODE_THEMATIC_BREAK,
+
+ <#...#>
+} cmark_node_type;
+```
+
+We can immediately see a few details that would need to be ironed out
+along the path of Swiftification ---
+notably,
+1\) the sentinel `NONE` value, which would instead be represented by `nil`, and
+2\) the aliases for the first and last block values,
+which wouldn't be encoded by distinct enumeration cases.
+
+Attempting to declare a Swift enumeration
+with a raw value type of `cmark_node_type` results in a compiler error.
+
+```swift
+enum NodeType: cmark_node_type {} // Error
+```
+
+However,
+that doesn't totally rule out `cmark_node_type` from being a `RawValue` type.
+Here's what we need to make that happen:
+
+```swift
+enum NodeType: RawRepresentable {
+ case document
+ case blockQuote
+ <#...#>
+
+ init?(rawValue: cmark_node_type) {
+ switch rawValue {
+ case CMARK_NODE_DOCUMENT: self = .document
+ case CMARK_NODE_BLOCK_QUOTE: self = .blockQuote
+ <#...#>
+ default:
+ return nil
+ }
+ }
+
+ var rawValue: cmark_node_type {
+ switch self {
+ case .document: return CMARK_NODE_DOCUMENT
+ case .blockQuote: return CMARK_NODE_BLOCK_QUOTE
+ <#...#>
+ }
+ }
+}
+```
+
+It's a far cry from being able to say `case document = CMARK_NODE_DOCUMENT`,
+but this approach offers a reasonable solution
+that falls within the existing semantics of the Swift standard library.
+
+{% info %}
+
+You can omit a protocol's associated type requirement
+if the type can be determined from the protocol's other requirements.
+
+For instance,
+the `RawRepresentable` protocol requires
+a `rawValue` property that returns a value of the associated `RawValue` type;
+a conforming type can implicitly satisfy the associated type requirement
+by declaring its property requirement with a concrete type
+(in the example above, `cmark_node_type`).
+
+{% endinfo %}
+
+That debunks the myth about
+`Int` and `String` being the only types that can be a raw value.
+What about that one about associated values?
+
+## RawRepresentable and Associated Values
+
+In Swift,
+an enumeration case can have one or more associated values.
+Associated values are a convenient way to introduce some flexibility
+into the closed semantics of enumerations
+and all the benefits they confer.
+
+[As the old adage goes](https://github.com/apple/swift/blob/master/docs/Driver.md#output-file-maps):
+
+> There are three numbers in computer science: 0, 1, and N.
+
+```swift
+enum Number {
+ case zero
+ case one
+ case n(Int)
+}
+```
+
+Because of the associated value on `n`,
+the compiler can't automatically synthesize an `Int` raw value type.
+But that doesn't mean we can't roll up our sleeves and pick up the slack.
+
+```swift
+extension Number: RawRepresentable {
+ init?(rawValue: Int) {
+ switch rawValue {
+ case 0: self = .zero
+ case 1: self = .one
+ case let n: self = .n(n)
+ }
+ }
+
+ var rawValue: Int {
+ switch self {
+ case .zero: return 0
+ case .one: return 1
+ case let .n(n): return n
+ }
+ }
+}
+
+Number(rawValue: 1) // .one
+```
+
+Another myth busted!
+
+Let's continue this example to clear up
+a misconception we found in the documentation.
+
+## RawRepresentable as Raw Values for Another Enumeration
+
+Consider the following from
+the `RawRepresentable` docs:
+
+> For any enumeration with a string, integer, or floating-point raw type,
+> the Swift compiler automatically adds `RawRepresentable` conformance.
+
+This is, strictly speaking, true.
+But it actually under-sells what the compiler can do.
+The actual requirements for raw values are as follows:
+
+- The raw value type must be `Equatable`
+- The raw value type must be
+ `ExpressibleByIntegerLiteral`,
+ `ExpressibleByFloatLiteral`, or
+ `ExpressibleByStringLiteral`
+- The raw value for each enumeration case must be a literal
+ (or unspecified, in which case the value is inferred)
+
+Let's see what happens if we satisfy that for our `Number` type from before.
+
+```swift
+extension Number: Equatable {} // conformance is automatically synthesized
+
+extension Number: ExpressibleByIntegerLiteral {
+ init(integerLiteral value: Int) {
+ self.init(rawValue: value)!
+ }
+}
+
+-1 as Number // .n(-1)
+0 as Number // .zero
+1 as Number // .one
+2 as Number // .n(2)
+```
+
+If we declare a new enumeration,
+数
+(literally "Number")
+with a `Number` raw value...
+
+```swift
+enum 数: Number {
+ case 一 = 1
+ case 二 = 2
+ case 三 = 3
+}
+
+数.二 // 二
+数.二.rawValue // .n(2)
+数.二.rawValue.rawValue // 2
+```
+
+_Wait, that actually works? Neat!_
+
+What's really interesting is that our contrived little enumeration type
+benefits from the same, small memory footprint
+that you get from using enumerations in more typical capacities:
+
+```swift
+MemoryLayout.size(ofValue: 数.三) // 1 (bytes)
+MemoryLayout.size(ofValue: 数.三.rawValue) // 9 (bytes)
+MemoryLayout.size(ofValue: 数.三.rawValue.rawValue) // 8 (bytes)
+```
+
+If raw values aren't limited to `String` or `Int`,
+as once believed,
+you may start to wonder:
+_How far can we take this?_
+
+## RawRepresentable with Metatype Raw Values
+
+Probably the biggest selling point of enumerations in Swift
+is how they encode a closed set of values.
+
+```swift
+enum Element {
+ case earth, water, air, fire
+}
+```
+
+Unfortunately,
+there's no equivalent way to "close off" which types conform to a protocol.
+
+```swift
+public protocol Elemental {}
+public struct Earth: Elemental {}
+public struct Water: Elemental {}
+public struct Air: Elemental {}
+public struct Fire: Elemental {}
+```
+
+Without built-in support for type unions
+or an analog to the `open` access modifier for classes,
+there's nothing that an API provider can do,
+for example,
+to prevent a consumer from doing the following:
+
+```swift
+struct Aether: Elemental {}
+```
+
+Any switch statement over a type-erased `Elemental` value
+using `is` checks will necessarily have a `default` case.
+
+Until we have a first-class language feature for providing such guarantees,
+we can recruit enumerations and raw values for a reasonable approximation:
+
+```swift
+extension Element: RawRepresentable {
+ init?(rawValue: Elemental.Type) {
+ switch rawValue {
+ case is Earth.Type:
+ self = .earth
+ case is Water.Type:
+ self = .water
+ case is Air.Type:
+ self = .air
+ case is Fire.Type:
+ self = .fire
+ default:
+ return nil
+ }
+ }
+
+ var rawValue: Elemental.Type {
+ switch self {
+ case .earth: return Earth.self
+ case .water: return Water.self
+ case .air: return Air.self
+ case .fire: return Fire.self
+ }
+ }
+}
+```
+
+{% warning %}
+
+This doesn't work for protocols with an associated type requirement.
+Sorry to disappoint anyone looking for an easy workaround for
+_"Protocol can only be used as a generic constraint
+because it has Self or associated type requirements"_
+
+{% endwarning %}
+
+--------------------------------------------------------------------------------
+
+Returning one last time to the docs,
+we're reminded that:
+
+> With a `RawRepresentable` type,
+> you can switch back and forth between
+> a custom type and an associated `RawValue` type
+> without losing the value of the original `RawRepresentable` type.
+
+From the earliest days of the language,
+`RawRepresentable` has been relegated to
+the thankless task of C interoperability.
+But looking now with a fresh set of eyes,
+we can now see it for in all its
+[injective](https://en.wikipedia.org/wiki/Injective_function) glory.
+
+So the next time you find yourself with an enumeration
+whose cases broker in discrete, defined counterparts,
+consider adopting `RawRepresentable` to formalize the connection.
diff --git a/2020-02-12-callable.md b/2020-02-12-callable.md
new file mode 100644
index 00000000..89b610e9
--- /dev/null
+++ b/2020-02-12-callable.md
@@ -0,0 +1,781 @@
+---
+title: Static and Dynamic Callable Types in Swift
+author: Mattt
+category: Swift
+excerpt: >-
+ If like most of us,
+ you aren't tuned into the comings-and-goings of Swift Evolution,
+ Xcode 11.4 is your first exposure to two new additions to the language:
+ key path expressions as functions and
+ callable values of user-defined nominal types.
+status:
+ swift: 5.2
+---
+
+Last week,
+Apple released the [first beta of Xcode 11.4][xcode 11.4 release notes],
+and it's proving to be one of the most substantial updates in recent memory.
+`XCTest` got [a huge boost][xcode 11.4 testing],
+with numerous quality of life improvements,
+and [Simulator][xcode 11.4 simulator], likewise, got a solid dose of
+TLC.
+But it's the changes to Swift that are getting the lion's share of attention.
+
+In Xcode 11.4,
+Swift compile times are down across the board,
+with many developers reporting improvements of 10 – 20% in their projects.
+And thanks to a [new diagnostics architecture][new diagnostics architecture],
+error messages from the compiler are consistently more helpful.
+This is also the first version of Xcode to ship with the new
+[`sourcekit-lsp` server](/language-server-protocol/),
+which serves to empower editors like [VSCode](/vscode/)
+to work with Swift in a more meaningful way.
+
+Yet,
+despite all of these improvements
+(which are truly an incredible achievement by Apple's Developer Tools team),
+much of the early feedback has focused on
+the most visible additions to Swift 5.2.
+And the response from the peanut galleries of
+Twitter, Hacker News, and Reddit has been —
+to put it charitably — _"mixed"_.
+
+* * *
+
+If like most of us,
+you aren't tuned into the comings-and-goings of [Swift Evolution][swift evolution],
+Xcode 11.4 was your first exposure to two new additions to the language:
+[key path expressions as functions][se-0249]
+and
+[callable values of user-defined nominal types][se-0253].
+
+The first of these allows key paths to replace
+one-off closures used by functions like `map`:
+
+```swift
+// Swift >= 5.2
+"🧁🍭🍦".unicodeScalars.map(\.properties.name)
+// ["CUPCAKE", "LOLLIPOP", "SOFT ICE CREAM"]
+
+// Swift <5.2 equivalent
+"🧁🍭🍦".unicodeScalars.map { $0.properties.name }
+```
+
+The second allows instances of types with a method named `callAsFunction`
+to be called as if they were a function:
+
+```swift
+struct Sweetener {
+ let additives: Set
+
+ init(_ sequence: S) where S: Sequence, S.Element == Character {
+ self.additives = Set(sequence)
+ }
+
+ func callAsFunction(_ message: String) -> String {
+ message.split(separator: " ")
+ .flatMap { [$0, "\(additives.randomElement()!)"] }
+ .joined(separator: " ") + "😋"
+ }
+}
+
+let dessertify = Sweetener("🧁🍭🍦")
+dessertify("Hello, world!")
+// "Hello, 🍭 world! 🍦😋"
+```
+
+* * *
+
+Granted,
+both of those examples are terrible.
+And that's kinda the problem.
+
+* * *
+
+Too often,
+coverage of _"What's New In Swift"_
+amounts to little more than a regurgitation of Swift Evolution proposals,
+interspersed with poorly motivated (and often emoji-laden) examples.
+Such treatments provide a poor characterization of Swift language features,
+and — in the case of Swift 5.2 —
+serves to feed into the popular critique that these are frivolous additions ---
+mere [syntactic sugar][syntactic sugar].
+
+
+
+This week,
+we hope to reach the ooey gooey center of the issue
+by providing some historical and theoretical context
+for understanding these new features.
+
+## Syntactic Sugar in Swift
+
+If you're salty about "key path as function" being too sugary,
+recall that the status quo
+isn't without a sweet tooth.
+Consider our saccharine example from before:
+
+```swift
+"🧁🍭🍦".unicodeScalars.map { $0.properties.name }
+```
+
+That expression relies on at least four different syntactic concessions:
+
+1. **Trailing closure syntax**,
+ which allows a final closure argument label of a function to be omitted
+2. **Anonymous closure arguments**,
+ which allow arguments in closures to be used positionally (`$0`, `$1`, ...)
+ without binding to a named variable.
+3. **Inferred parameter and return value types**
+4. **Implicit return from single-expression closures**
+
+If you wanted to cut sugar out of your diet completely,
+you'd best get [Mavis Beacon][mavis beacon teaches typing] on the line,
+because you'll be doing a lot more [typing](/rawrepresentable/).
+
+```swift
+"🧁🍭🍦".unicodeScalars.map(transform: { (unicodeScalar: Unicode.Scalar) -> String in
+ return unicodeScalar.properties.name
+})
+```
+
+
+
+In fact,
+as we'll see in the examples to come,
+Swift is a marshmallow world in the winter,
+_syntactically speaking_.
+From initializers and method calls to optionals and method chaining,
+nearly everything about Swift could be described as a cotton candy melody —
+it really just depends on where you draw the line between
+"language feature" and "syntactic sugar".
+
+* * *
+
+To understand why,
+you have to understand how we got here in the first place,
+which requires a bit of history, math, and computer science.
+Get ready to eat your vegetables 🥦.
+
+## The λ-Calculus and Speculative Computer Science Fiction
+
+All programming languages can be seen as various attempts to represent
+[the λ-calculus][lambda calculus].
+Everything you need to write code —
+variables, binding, application —
+it's all in there,
+buried under a mass of Greek letters and mathematical notation.
+
+Setting aside syntactic differences,
+each programming language can be understood by
+its combination of affordances for
+making programs easier to write and easier to read.
+Language features like
+objects,
+classes,
+modules,
+optionals,
+literals,
+and generics
+are all just abstractions built on top of the λ-calculus.
+
+Any other deviation from pure mathematical formalism
+can be ascribed to real-world constraints,
+such as
+[a typewriter from the 1870s][qwerty],
+[a punch card from the 1920s][punch cards],
+[a computer architecture from the 1940s][von neumann],
+or [a character encoding from the 1960s][ascii].
+
+Among the earliest programming languages were Lisp, ALGOL*, and COBOL,
+from which nearly every other language derives.
+
+
+
+
+```lisp
+(defun square (x)
+ (* x x))
+
+(print (square 4))
+;; 16
+```
+
+```fortran
+pure function square(x)
+ integer, intent(in) :: x
+ integer :: square
+ square = x * x
+end function
+
+program main
+ integer :: square
+ print *, square(4)
+end program main
+! 16
+```
+
+```cobol
+IDENTIFICATION DIVISION.
+ PROGRAM-ID. example.
+ DATA DIVISION.
+ WORKING-STORAGE SECTION.
+ 01 x PIC 9(3) VALUE 4.
+ 01 y PIC 9(9).
+ PROCEDURE DIVISION.
+ CALL "square" USING
+ BY CONTENT x
+ BY REFERENCE y.
+ DISPLAY y.
+ STOP RUN.
+ END PROGRAM example.
+
+IDENTIFICATION DIVISION.
+ PROGRAM-ID. square.
+ DATA DIVISION.
+ LINKAGE SECTION.
+ 01 x PIC 9(3).
+ 01 y PIC 9(3).
+ PROCEDURE DIVISION USING x, y.
+ MULTIPLY x BY x GIVING y.
+ EXIT PROGRAM.
+ END PROGRAM square.
+* 016000000
+```
+
+Here you get a glimpse into three very different timelines;
+ours is the reality in which ALGOL's syntax (option #2)
+"won out" over the alternatives.
+From ALGOL 60,
+you can draw a straight line from
+[CPL][cpl] in 1963,
+to [BCPL][bcpl] in 1967
+and [C] in 1972,
+followed by [Objective-C in 1984](/direct/#object-oriented-programming)
+and Swift in 2014.
+That's the lineage that informs what types are callable and how we call them.
+
+* * *
+
+_Now, back to Swift..._
+
+## Function Types in Swift
+
+Functions are first-class objects in Swift,
+meaning that they can be assigned to variables,
+stored in properties,
+and passed as arguments or returned as values from other functions.
+
+What distinguishes function types from other values
+is that they're callable,
+meaning that you can invoke them to produce new values.
+
+### Closures
+
+Swift's fundamental function type is the closure,
+a self-contained unit of functionality.
+
+```swift
+let square: (Int) -> Int = { x in x * x }
+```
+
+As a function type,
+you can call a closure by passing the requisite number of arguments
+between opening and closing parentheses `()` —
+a la ALGOL.
+
+```swift
+square(4) // 16
+```
+
+{% info %}
+
+The number of arguments taken by a function type
+is known as its arity.
+
+{% endinfo %}
+
+Closures are so called because they close over and capture
+references to any variables from the context in which they're defined.
+However, capturing semantics aren't always desirable,
+which is why Swift provides dedicated syntax to a special kind of closure
+known as a function.
+
+### Functions
+
+Functions defined at a top-level / global scope
+are named closures that don't capture any values.
+In Swift,
+you declare them with the `func` keyword:
+
+```swift
+func square(_ x: Int) -> Int { x * x }
+square(4) // 16
+```
+
+Compared to closures,
+functions have greater flexibility in how arguments are passed.
+
+Function arguments can have named labels
+instead of a closure's unlabeled, positional arguments —
+which goes a long way to clarify the effect of code at its call site:
+
+```swift
+func deposit(amount: Decimal,
+ from source: Account,
+ to destination: Account) throws { <#...#> }
+try deposit(amount: 1000.00, from: checking, to: savings)
+```
+
+Functions can be [generic][generics],
+allowing them to be used for multiple types of arguments:
+
+```swift
+func square(_ x: T) -> T { x * x }
+func increment(_ x: T) -> T { x + 1 }
+func compose(_ f: @escaping (T) -> T, _ g: @escaping (T) -> T) -> (T) -> T {
+ { x in g(f(x)) }
+}
+
+compose(increment, square)(4 as Int) // 25 ((4 + 1)²)
+compose(increment, square)(4.2 as Double) // 27.04 ((4.2 + 1)²)
+```
+
+Functions can also take variadic arguments,
+implicit closures,
+and default argument values
+(allowing for magic expression literals like `#file` and `#line`):
+
+```swift
+func print(items: Any...) { <#...#> }
+
+func assert(_ condition: @autoclosure () -> Bool,
+ _ message: @autoclosure () -> String = String(),
+ file: StaticString = #file,
+ line: UInt = #line) { <#...#> }
+```
+
+And yet,
+despite all of this flexibility for accepting arguments,
+most functions you'll encounter operate on an _implicit_ `self` argument.
+These functions are called methods.
+
+### Methods
+
+A method is a function contained by a type.
+Methods automatically provide access to `self`,
+allowing them to effectively capture the instance on which they're called
+as an implicit argument.
+
+```swift
+struct Queue {
+ private var elements: [Element] = []
+
+ mutating func push(_ newElement: Element) {
+ self.elements.append(newElement)
+ }
+
+ mutating func pop() -> Element? {
+ guard !self.elements.isEmpty else { return nil }
+ return self.elements.removeFirst()
+ }
+}
+```
+
+{% info %}
+
+Swift goes one step further
+by allowing `self.` to be omitted for member access —
+making the already implicit `self` all the more implicit.
+
+{% endinfo %}
+
+* * *
+
+Putting everything together,
+these syntactic affordances allow Swift code to be
+expressive, clear, and concise:
+
+```swift
+var queue = Queue()
+queue.push(1)
+queue.push(2)
+queue.pop() // 1
+```
+
+Compared to more verbose languages like Objective-C,
+the experience of writing Swift is, well, pretty _sweet_.
+It's hard to imagine any Swift developers objecting to what we have here
+as being "sugar-coated".
+
+But like a 16oz can of [Surge][surge],
+the sugar content of something is often surprising.
+Turns out,
+that example from before is far from innocent:
+
+```swift
+var queue = Queue() // desugars to `Queue.init()`
+queue.push(1) // desugars to `Queue.push(&queue)(1)`
+```
+
+All this time,
+our so-called "direct" calls to methods and initializers
+were actually shorthand for [function currying][currying]
+[partially-applied functions][partial application].
+
+{% info %}
+
+Partial application and currying are often conflated.
+In fact,
+[they're distinct but related concepts](https://ericasadun.com/2017/04/03/musings-on-partial-application/).
+
+Early versions of Swift had a dedicated syntax for currying functions,
+but it proved less useful than originally anticipated
+and was removed by the [second-ever Swift Evolution proposal][se-0002].
+
+```swift
+// Swift <3:
+func curried(x: Int)(y: String) -> Float {
+ return Float(x) + Float(y)!
+}
+
+// Swift >=3
+func curried(x: Int) -> (String) -> Float {
+ return { (y: String) -> Float in
+ return Float(x) + Float(y)!
+ }
+}
+```
+
+{% endinfo %}
+
+With this in mind,
+let's now take another look at callable types in Swift more generally.
+
+## {Type, Instance, Member} ⨯ {Static, Dynamic}
+
+Since their introduction in Swift 4.2 and Swift 5, respectively,
+many developers have had a hard time keeping
+`@dynamicMemberLookup` and `@dynamicCallable`
+straight in their minds —
+made even more difficult by the introduction of `callAsFunction` in Swift 5.2.
+
+If you're also confused,
+we think the following table can help clear things up:
+
+| | Static | Dynamic |
+|-------------|----------------------|----------------------------|
+| Type | `init` | _N/A_ |
+| Instance | **`callAsFunction`** | **`@dynamicCallable`** |
+| Member | `func` | **`@dynamicMemberLookup`** |
+
+Swift has always had static callable types and type members.
+What's changed in new versions of Swift
+is that instances are now callable,
+and both instances and members can now be called dynamically.
+
+{% warning %}
+
+You might have noticed the blank spot in our table.
+Indeed, there's no way to dynamically call types.
+In fact, there's no way to statically call types
+other than to invoke initializers —
+and that's probably for the best.
+
+{% endwarning %}
+
+Let's see what that means in practice,
+starting with static callables.
+
+### Static Callable
+
+```swift
+struct Static {
+ init() {}
+
+ func callAsFunction() {}
+
+ static func function() {}
+ func function() {}
+}
+```
+
+This type can be called statically in the following ways:
+
+```swift
+let instance = Static() // <#❶#> desugars to `Static.init()`
+
+Static.function() // <#❷#> (no syntactic sugar!)
+instance.function() // <#❸#> desugars to Static.function(instance)()
+
+instance() // <#❹#> desugars to `Static.callAsFunction(instance)()`
+```
+
+❶
+: Calling the `Static` type invokes an initializer
+
+❷
+: Calling `function` on the `Static` type
+ invokes the corresponding static function member,
+ passing `Static` as an implicit `self` argument.
+
+❸
+: Calling `function` on an instance of `Static`
+ invokes the corresponding function member,
+ passing the instance as an implicit `self` argument.
+
+❹
+: Calling an instance of `Static`
+ invokes the `callAsFunction()` function member,
+ passing the instance as an implicit `self` argument.
+
+{% info %}
+
+A few points for completeness' sake:
+
+- You can also statically call subscripts and variable members (properties).
+- Operators provide an alternative way to invoke static member functions.
+- Enumeration cases are, well...
+ something else entirely.
+
+
+
+{% endinfo %}
+
+### Dynamic Callable
+
+```swift
+@dynamicCallable
+@dynamicMemberLookup
+struct Dynamic {
+ func dynamicallyCall(withArguments args: [Int]) -> Void { () }
+ func dynamicallyCall(withKeywordArguments args: KeyValuePairs) -> Void { () }
+
+ static subscript(dynamicMember member: String) -> (Int) -> Void { { _ in } }
+ subscript(dynamicMember member: String) -> (Int) -> Void { { _ in } }
+}
+```
+
+This type can be called dynamically in a few different ways:
+
+```swift
+let instance = Dynamic() // desugars to `Dynamic.init()`
+
+instance(1) // <#❶#> desugars to `Dynamic.dynamicallyCall(instance)(withArguments: [1])`
+instance(a: 1) // <#❷#> desugars to `Dynamic.dynamicallyCall(instance)(withKeywordArguments: ["a": 1])`
+
+Dynamic.function(1) // <#❸#> desugars to `Dynamic[dynamicMember: "function"](1)`
+instance.function(1) // <#❹#> desugars to `instance[dynamicMember: "function"](1)`
+```
+
+❶
+: Calling an instance of `Dynamic`
+ invokes the `dynamicallyCall(withArguments:)` method,
+ passing an array of arguments
+ and `Dynamic` as an implicit `self` argument.
+
+❷
+: Calling an instance of `Dynamic`
+ with at least one labeled argument
+ invokes the `dynamicallyCall(withKeywordArguments:)` method,
+ passing the arguments in a [`KeyValuePairs` object](/keyvaluepairs/)
+ and `Dynamic` as an implicit `self` argument.
+
+❸
+: Calling `function` on the `Dynamic` type
+ invokes the static `dynamicMember` subscript,
+ passing `"function"` as the key;
+ here, we call the returned anonymous closure.
+
+❹
+: Calling `function` on an instance of `Dynamic`
+ invokes the `dynamicMember` subscript,
+ passing `"function"` as the key;
+ here, we call the returned anonymous closure.
+
+#### Dynamism by Declaration Attributes
+
+`@dynamicCallable` and `@dynamicMemberLookup`
+are declaration attributes,
+which means that they can't be applied to existing declarations
+through an extension.
+
+So you can't, for example,
+_spice up_ `Int` with [Ruby-ish][rails core_ext array]
+natural language accessors:
+
+```swift
+@dynamicMemberLookup // ⚠︎ Error: '@dynamicMemberLookup' attribute cannot be applied to this declaration
+extension Int {
+ static subscript(dynamicMember member: String) -> Int? {
+ let string = member.replacingOccurrences(of: "_", with: "-")
+
+ let formatter = NumberFormatter()
+ formatter.numberStyle = .spellOut
+ return formatter.number(from: string)?.intValue
+ }
+}
+
+// ⚠︎ Error: Just to be super clear, this doesn't work
+Int.forty_two // 42 (hypothetically, if we could apply `@dynamicMemberLookup` in an extension)
+```
+
+Contrast this with `callAsFunction`,
+which can be added to any type in an extension.
+
+{% info %}
+
+For more information about these new language features,
+check out the original Swift Evolution proposals:
+
+- [SE-0195: Introduce User-defined "Dynamic Member Lookup" Types][se-0195]
+- [SE-0216: Introduce - user-defined dynamically "callable" types][se-0216]
+- [SE-0249: Key Path Expressions as Functions][se-0249]
+- [SE-0252: Key - Path Member Lookup][se-0252]
+- [SE-0253: Callable values of user-defined nominal types][se-0253]
+
+{% endinfo %}
+
+* * *
+
+There's much more to talk about with
+`@dynamicMemberLookup`, `@dynamicCallable`, and `callAsFunction`,
+and we look forward to covering them all in more detail
+in future articles.
+
+* * *
+
+_But speaking of RubyPython..._
+
+## Swift ⨯ ___________________
+
+Adding to
+[our list of _"What code is like"_](/numericcast/):
+
+> Code is like fan fiction.
+
+Sometimes to ship software,
+you need to pair up and "ship" different technologies.
+
+
+
+In building these features,
+the "powers that be" have ordained that
+[Swift replace Python for Machine Learning][s4tf].
+Taking for granted that an incremental approach is best,
+the way to make that happen is to allow
+Swift to interoperate with Python
+as seamlessly as it does with Objective-C.
+And since Swift 4.2,
+we've been [getting pretty close][python interop].
+
+```swift
+import Python
+
+let numpy = Python.import("numpy")
+let zeros = numpy.ones([2, 4])
+/* [[1, 1, 1, 1]
+ [1, 1, 1, 1]] */
+```
+
+## The Externalities of Dynamism
+
+The promise of additive changes is that they don't change anything
+if you don't want them to.
+You can continue to write Swift code
+remaining totally ignorant of the features described in this article
+(most of us have so far).
+But let's be clear:
+there are no cost-free abstractions.
+
+Economics uses the term [negative externalities][externality]
+to describe indirect costs incurred by a decision.
+Although you don't pay for these features unless you use them,
+we all shoulder the burden of a more complex language
+that's more difficult to teach, learn, document, and reason about.
+
+* * *
+
+A lot of us who have been with Swift from the beginning
+have grown weary of Swift Evolution.
+And for those on the outside looking in,
+it's unfathomable that we're wasting time on inconsequential "sugar" like this
+instead of features that will _really_ move the needle,
+like [`async` / `await`][async await].
+
+In isolation,
+each of these proposals is thoughtful and useful — _genuinely_.
+We've already [had occasion][dbscan] to use a few of them.
+But it can be really hard to judge things on their own technical merits
+when they're steeped in emotional baggage.
+
+Everyone has their own sugar tolerance,
+and it's often informed by what they're accustomed to.
+Being cognizant of the [drawbridge effect][drawbridge effect],
+I honestly can't tell if I'm out of touch,
+or if it's [the children who are wrong][skinner]...
+
+[se-0002]: https://github.com/apple/swift-evolution/blob/master/proposals/0002-remove-currying.md "SE-0002: Removing currying func declaration syntax"
+[se-0195]: https://github.com/apple/swift-evolution/blob/master/proposals/0195-dynamic-member-lookup.md "SE-0195: Introduce User-defined \"Dynamic Member Lookup\" Types"
+[se-0216]: https://github.com/apple/swift-evolution/blob/master/proposals/0216-dynamic-callable.md "SE-0216: Introduce user-defined dynamically \"callable\" types"
+[se-0249]: https://github.com/apple/swift-evolution/blob/master/proposals/0249-key-path-literal-function-expressions.md "SE-0249: Key Path Expressions as Functions"
+[se-0252]: https://github.com/apple/swift-evolution/blob/master/proposals/0252-keypath-dynamic-member-lookup.md "SE-0252: Key Path Member Lookup"
+[se-0253]: https://github.com/apple/swift-evolution/blob/master/proposals/0253-callable.md "SE-0253: Callable values of user-defined nominal types"
+
+[xcode 11.4 release notes]: https://developer.apple.com/documentation/xcode_release_notes/xcode_11_4_beta_release_notes
+[xcode 11.4 testing]: https://developer.apple.com/documentation/xcode_release_notes/xcode_11_4_beta_release_notes#3530390
+[xcode 11.4 simulator]: https://developer.apple.com/documentation/xcode_release_notes/xcode_11_4_beta_release_notes#3530393
+
+[new diagnostics architecture]: https://swift.org/blog/new-diagnostic-arch-overview/ "Swift.org - New Diagnostic Architecture Overview"
+[swift evolution]: https://apple.github.io/swift-evolution/ "Swift Evolution Proposals Dashboard"
+[syntactic sugar]: https://en.wikipedia.org/wiki/Syntactic_sugar
+[church-turing thesis]: https://en.wikipedia.org/wiki/Church–Turing_thesis "Church–Turing Thesis"
+[lambda calculus]: https://en.wikipedia.org/wiki/Lambda_calculus
+[qwerty]: https://en.wikipedia.org/wiki/QWERTY "QWERTY"
+[punch cards]: https://en.wikipedia.org/wiki/Punched_card#IBM_80-column_punched_card_format_and_character_codes
+[ascii]: https://en.wikipedia.org/wiki/ASCII
+[von neumann]: https://en.wikipedia.org/wiki/Von_Neumann_architecture "Von Neumann Architecture"
+[piet]: https://en.wikipedia.org/wiki/Esoteric_programming_language#Piet
+[cpl]: https://en.wikipedia.org/wiki/CPL_(programming_language)
+[bcpl]: https://en.wikipedia.org/wiki/BCPL
+[c]: https://en.wikipedia.org/wiki/C_(programming_language)
+[currying]: https://en.wikipedia.org/wiki/Currying
+[partial application]: https://en.wikipedia.org/wiki/Partial_application
+[mavis beacon teaches typing]: https://en.wikipedia.org/wiki/Mavis_Beacon_Teaches_Typing "Mavis Beacon Teaches Typing"
+[generics]: https://docs.swift.org/swift-book/LanguageGuide/Generics.html "The Swift Programming Language - Generics"
+[surge]: https://en.wikipedia.org/wiki/Surge_(drink) "SURGE"
+[rails core_ext array]: https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/array/access.r
+[s4tf]: https://github.com/tensorflow/swift/blob/master/docs/WhySwiftForTensorFlow.md
+[python interop]: https://www.tensorflow.org/swift/tutorials/python_interoperability
+[externality]: https://en.wikipedia.org/wiki/Externality
+[dynamic swift thread]: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160926/027337.html
+[async await]: https://gist.github.com/lattner/429b9070918248274f25b714dcfc7619
+[dbscan]: https://github.com/NSHipster/DBSCAN
+[drawbridge effect]: https://en.wikipedia.org/wiki/Drawbridge_mentality
+[skinner]: https://knowyourmeme.com/memes/am-i-out-of-touch
+
+{% asset 'articles/callable.css' %}
diff --git a/2020-02-27-xcconfig.md b/2020-02-27-xcconfig.md
new file mode 100644
index 00000000..9feb068b
--- /dev/null
+++ b/2020-02-27-xcconfig.md
@@ -0,0 +1,450 @@
+---
+title: Xcode Build Configuration Files
+author: Mattt
+category: Xcode
+excerpt: >-
+ Software development best practices prescribe
+ strict separation of configuration from code.
+ Learn how you can use `xcconfig` files
+ to make your Xcode projects more compact, comprehensible, and powerful.
+status:
+ swift: 5.2
+revisions:
+ 2019-05-13: Initial Publication
+ 2020-02-27: Updated for Xcode 11.4
+---
+
+Software development best practices
+[prescribe](https://12factor.net/config)
+strict separation of configuration from code.
+Yet developers on Apple platforms
+often struggle to square these guidelines with Xcode's project-heavy workflow.
+
+Understanding what each project setting does
+and how they all interact with one another
+is a skill that can take years to hone.
+And the fact that much of this information
+is buried deep within the GUIs of Xcode does us no favors.
+
+Navigate to the "Build Settings" tab of the project editor,
+and you'll be greeted by _hundreds_ of build settings
+spread across layers of projects, targets, and configurations ---
+_and that's to say nothing of the other six tabs!_
+
+
+
+
+
+
+Fortunately,
+there's a better way to manage all of this configuration
+that doesn't involve clicking through a maze of tabs and disclosure arrows.
+
+This week,
+we'll show you how you can use text-based `xcconfig` files
+to externalize build settings from Xcode
+to make your projects more compact, comprehensible, and powerful.
+
+{% info %}
+
+Check out [XcodeBuildSettings.com](https://xcodebuildsettings.com)
+for a complete reference of every build setting
+supported for the latest version of Xcode.
+
+{% endinfo %}
+
+* * *
+
+[Xcode build configuration files](https://help.apple.com/xcode/mac/current/#/dev745c5c974),
+more commonly known by their `xcconfig` file extension,
+allow build settings for your app to be declared and managed without Xcode.
+They're plain text,
+which means they're much friendlier to source control systems
+and can be modified with any editor.
+
+Fundamentally,
+each configuration file consists of a sequence of key-value assignments
+with the following syntax:
+
+```xcconfig
+<#BUILD_SETTING_NAME#> = <#value#>
+```
+
+For example,
+to specify the Swift language version for a project,
+you'd specify the `SWIFT_VERSION` build setting like so:
+
+```xcconfig
+SWIFT_VERSION = 5.0
+```
+
+{% info %}
+
+According to the [POSIX standard](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08)
+environment variables have names consisting solely of
+uppercase letters, digits, and underscore (`_`) —
+a convention I like to call `SCREAMING_SNAKE_CASE` 🐍🗯.
+
+{% endinfo %}
+
+* * *
+
+At first glance,
+`xcconfig` files bear a striking resemblance to `.env` files,
+with their simple, newline-delimited syntax.
+But there's more to Xcode build configuration files than meets the eye.
+_Behold!_
+
+### Retaining Existing Values
+
+To append rather than replace existing definitions,
+use the `$(inherited)` variable like so:
+
+```xcconfig
+<#BUILD_SETTING_NAME#> = $(inherited)<#additional value#>
+```
+
+You typically do this to build up lists of values,
+such as the paths in which
+the compiler searches for frameworks
+to find included header files
+(`FRAMEWORK_SEARCH_PATHS`):
+
+```xcconfig
+FRAMEWORK_SEARCH_PATHS = $(inherited) $(PROJECT_DIR)
+```
+
+Xcode assigns inherited values in the following order
+(from lowest to highest precedence):
+
+- Platform Defaults
+- Xcode Project xcconfig File
+- Xcode Project File Build Settings
+- Target xcconfig File
+- Target Build Settings
+
+{% info %}
+
+Spaces are used to delimit items in string and path lists.
+To specify an item containing whitespace,
+you must enclose it with quotation marks (`"`).
+
+{% endinfo %}
+
+### Referencing Values
+
+You can substitute values from other settings
+by their declaration name
+with the following syntax:
+
+```xcconfig
+<#BUILD_SETTING_NAME#> = $(<#ANOTHER_BUILD_SETTING_NAME#>)
+```
+
+Substitutions can be used to
+define new variables according to existing values,
+or inline to build up new values dynamically.
+
+```xcconfig
+OBJROOT = $(SYMROOT)
+CONFIGURATION_BUILD_DIR = $(BUILD_DIR)/$(CONFIGURATION)-$(PLATFORM_NAME)
+```
+
+### Setting Fallback Values for Referenced Build Settings
+
+In Xcode 11.4 and later,
+you can use the `default` evaluation operator
+to specify a fallback value to use
+if the referenced build setting evaluates as empty.
+
+```xcconfig
+$(<#BUILD_SETTING_NAME#>:default=<#value#>)
+```
+
+### Conditionalizing Build Settings
+
+You can conditionalize build settings according to their
+SDK (`sdk`), architecture (`arch`), and / or configuration (`config`)
+according to the following syntax:
+
+```xcconfig
+<#BUILD_SETTING_NAME#>[sdk=<#sdk#>] = <#value for specified sdk#>
+<#BUILD_SETTING_NAME#>[arch=<#architecture#>] = <#value for specified architecture#>
+<#BUILD_SETTING_NAME#>[config=<#configuration#>] = <#value for specified configuration#>
+```
+
+Given a choice between multiple definitions of the same build setting,
+the compiler resolves according to specificity.
+
+```xcconfig
+<#BUILD_SETTING_NAME#>[sdk=<#sdk#>][arch=<#architecture#>] = <#value for specified sdk and architectures#>
+<#BUILD_SETTING_NAME#>[sdk=*][arch=<#architecture#>] = <#value for all other sdks with specified architecture#>
+```
+
+For example,
+you might specify the following build setting
+to speed up local builds by only compiling for the active architecture:
+
+```xcconfig
+ONLY_ACTIVE_ARCH[config=Debug][sdk=*][arch=*] = YES
+```
+
+### Including Build Settings from Other Configuration Files
+
+A build configuration file can include settings from other configuration files
+using the same `#include` syntax
+as the equivalent `C` directive
+on which this functionality is based:
+
+```xcconfig
+#include "<#path/to/File.xcconfig#>"
+```
+
+As we'll see later on in the article,
+you can take advantage of this to build up cascading lists of build settings
+in really powerful ways.
+
+{% info %}
+Normally when the compiler encounters an `#include` directive
+that can't be resolved,
+it raises an error.
+But `xcconfig` files also support an `#include?` directive,
+that doesn't complain if the file can't be found.
+
+There aren't many cases in which you'd want
+the existence or nonexistence of a file
+to change compile-time behavior;
+after all, builds are best when they're predictable.
+But you might use this
+as a hook for optional development tools like [Reveal](https://revealapp.com/),
+which requires the following configuration:
+
+```xcconfig
+# Reveal.xcconfig
+OTHER_LDFLAGS = $(inherited) -weak_framework RevealServer
+FRAMEWORK_SEARCH_PATHS = $(inherited) /Applications/Reveal.app/Contents/SharedSupport/iOS-Libraries
+```
+
+{% endinfo %}
+
+## Creating Build Configuration Files
+
+To create a build configuration file,
+select the "File > New File..." menu item (⌘N),
+scroll down to the section labeled "Other",
+and select the Configuration Settings File template.
+Next, save it somewhere in your project directory,
+making sure to add it to your desired targets
+
+
+
+
+
+
+Once you've created an `xcconfig` file,
+you can assign it to one or more build configurations
+for its associated targets.
+
+
+
+
+
+
+{% info %}
+
+Build configuration files shouldn't be included in any of your project's targets.
+If you find any `.xcconfig` files showing up in your app's `.ipa` archive,
+make sure that they aren't a member of any targets
+and don't appear in any "Copy Bundle Resources" build phases.
+
+{% endinfo %}
+
+* * *
+
+Now that we've covered the basics of using Xcode build configuration files
+let's look at a couple of examples of how you can use them
+to manage development, stage, and production environments.
+
+* * *
+
+## Customizing App Name and Icon for Internal Builds
+
+Developing an iOS app usually involves
+juggling various internal builds
+on your simulators and test devices
+(as well as the latest version from the App Store,
+to use as a reference).
+
+You can make things easier on yourself
+with `xcconfig` files that assign each configuration
+a distinct name and app icon.
+
+```xcconfig
+// Development.xcconfig
+PRODUCT_NAME = $(inherited) α
+ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-Alpha
+
+//////////////////////////////////////////////////
+
+// Staging.xcconfig
+PRODUCT_NAME = $(inherited) β
+ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-Beta
+```
+
+## Managing Constants Across Different Environments
+
+If your backend developers comport themselves according to the aforementioned
+[12 Factor App](https://12factor.net/config) philosophy,
+then they'll have separate endpoints for
+development, stage, and production environments.
+
+On iOS,
+perhaps the most common approach to managing these environments
+is to use conditional compilation statements
+with build settings like `DEBUG`.
+
+```swift
+import Foundation
+
+#if DEBUG
+let apiBaseURL = URL(string: "https://api.staging.example.com")!
+#else
+let apiBaseURL = URL(string: "https://api.example.com")!
+#endif
+```
+
+This gets the job done,
+but runs afoul of the canon of code / configuration separation.
+
+An alternative approach takes these environment-specific values
+and puts them where they belong —
+into `xcconfig` files.
+
+```xcconfig
+// Development.xcconfig
+API_BASE_URL = api.staging.example.com
+
+//////////////////////////////////////////
+
+// Production.xcconfig
+API_BASE_URL = api.example.com
+```
+
+{% warning %}
+
+`xcconfig` files treat the sequence
+`//` as a comment delimiter,
+regardless of whether it's enclosed in quotation marks.
+If you try to escape with backslashes `\/\/`,
+those backslashes show up literally
+and must be removed from the resulting value.
+This is especially inconvenient when specifying per-environment URL constants.
+
+If you'd rather not work around this unfortunate behavior,
+you can always omit the scheme and prepend `https://` in code.
+_(You are using https... right?)_
+
+{% endwarning %}
+
+However,
+to pull these values programmatically,
+we'll need to take one additional step:
+
+### Accessing Build Settings from Swift
+
+Build settings defined by
+the Xcode project file, `xcconfig` files, and environment variables,
+are only available at build time.
+When you run the compiled app,
+none of that surrounding context is available.
+_(And thank goodness for that!)_
+
+But wait a sec —
+don't you remember seeing some of those build settings before
+in one of those other tabs?
+Info, was it?
+
+As it so happens,
+that info tab is actually just a fancy presentation of
+the target's `Info.plist` file.
+At build time,
+that `Info.plist` file is compiled
+according to the build settings provided
+and copied into the resulting app [bundle](/bundles-and-packages/).
+Therefore,
+by adding references to `$(API_BASE_URL)`,
+you can access the values for those settings
+through the `infoDictionary` property of Foundation's `Bundle` API.
+_Neat!_
+
+
+
+
+
+
+Following this approach,
+we might do something like the following:
+
+```swift
+import Foundation
+
+enum Configuration {
+ enum Error: Swift.Error {
+ case missingKey, invalidValue
+ }
+
+ static func value(for key: String) throws -> T where T: LosslessStringConvertible {
+ guard let object = Bundle.main.object(forInfoDictionaryKey:key) else {
+ throw Error.missingKey
+ }
+
+ switch object {
+ case let value as T:
+ return value
+ case let string as String:
+ guard let value = T(string) else { fallthrough }
+ return value
+ default:
+ throw Error.invalidValue
+ }
+ }
+}
+
+enum API {
+ static var baseURL: URL {
+ return try! URL(string: "https://" + Configuration.value(for: "API_BASE_URL"))!
+ }
+}
+```
+
+When viewed from the call site,
+we find that this approach harmonizes beautifully
+with our best practices —
+not a single hard-coded constant in sight!
+
+```swift
+let url = URL(string: path, relativeTo: API.baseURL)!
+var request = URLRequest(url: url)
+request.httpMethod = method
+```
+
+{% error %}
+
+Don't use `xcconfig` files to store secrets like API keys or other credentials
+For more information,
+please refer to our article about [secret management on iOS](/secrets/).
+
+{% enderror %}
+
+* * *
+
+Xcode projects are monolithic, fragile, and opaque.
+They're a source of friction for collaboration among team members
+and generally a drag to work with.
+
+Fortunately,
+`xcconfig` files go a long way to address these pain points.
+Moving configuration out of Xcode and into `xcconfig` files
+confers a multitude of benefits
+and offers a way to distance your project from the particulars of Xcode
+without leaving the Cupertino-approved "happy path".
diff --git a/2020-03-26-swift-log.md b/2020-03-26-swift-log.md
new file mode 100644
index 00000000..d9f184ad
--- /dev/null
+++ b/2020-03-26-swift-log.md
@@ -0,0 +1,676 @@
+---
+title: Swift Logging
+author: Mattt
+category: Swift
+excerpt: >-
+ Developed by the Swift on Server community,
+ its benefit isn’t limited to use on the server.
+ Indeed, any Swift code intended to be run from the command line
+ would benefit from adopting SwiftLog.
+status:
+ swift: 5.2
+---
+
+In 2002,
+the United States Congress enacted
+the [Sarbanes–Oxley Act](https://en.wikipedia.org/wiki/Sarbanes–Oxley_Act),
+which introduced broad oversight to corporations
+in response to accounting scandals at companies like
+[Enron](https://en.wikipedia.org/wiki/Enron_scandal) and
+[MCI WorldCom](https://en.wikipedia.org/wiki/MCI_Inc.#Accounting_scandals)
+around that time.
+This act,
+
+PCI
+
+and
+
+
+HIPAA
+
+,
+formed the regulatory backdrop
+for a new generation of
+IT companies
+emerging from the [dot-com bubble](https://en.wikipedia.org/wiki/Dot-com_bubble).
+
+Around the same time,
+we saw the emergence of ephemeral, distributed infrastructure —
+what we now call ["Cloud computing"](https://en.wikipedia.org/wiki/Cloud_computing) —
+a paradigm that made systems more capable but also more complex.
+
+To solve both the regulatory and logistical challenges of the 21st century,
+our field established best practices around application logging.
+And many of the same tools and standards are still in use today.
+
+
+
+{% info %}
+
+_Sarbanes–Oxley_ is notable for giving rise to
+_[Yates v. United States](https://www.oyez.org/cases/2014/13-7451)_:
+a delightful Supreme Court case that asked the question
+_"Are fish (🐟) tangible objects?"
+
+Although the Court found in a 5 – 4 decision
+that fish are _not_, in fact, "tangible objects"
+(for purposes of the statute),
+we remain unconvinced
+for the same reasons articulated in
+[Justice Kagan's dissent](https://supreme.justia.com/cases/federal/us/574/528/#tab-opinion-3243031)
+_(and pretty much anything written by
+[Claude Shannon](https://en.wikipedia.org/wiki/Claude_Shannon),
+for that matter)_.
+
+{% endinfo %}
+
+* * *
+
+This week on NSHipster,
+we're taking a look at
+[`SwiftLog`](https://github.com/apple/swift-log):
+a community-driven, open-source standard for logging in Swift.
+
+Developed by the Swift on Server community
+and endorsed by the
+[SSWG (Swift Server Work Group)](https://swift.org/server/),
+its benefit isn't limited to use on the server.
+Indeed,
+any Swift code intended to be run from the command line
+would benefit from adopting `SwiftLog`.
+Read on to learn how.
+
+{% warning %}
+
+`SwiftLog` is distinct from the
+[Unified Logging System](https://developer.apple.com/documentation/os/logging)
+(`os_log`),
+which is specific to Apple platforms.
+
+Readers may already be familiar with `os_log` and its, _\*ahem\*_
+[quirks](https://mjtsai.com/blog/2019/03/06/problems-with-os_log/)
+_(a topic for a future article, perhaps)_.
+But for the uninitiated,
+all you need to know is that
+`os_log` is for macOS and iOS apps and
+`SwiftLog` is for everything else.
+
+If you're interested in learning more about Unified Logging,
+you can get a quick overview of
+by skimming the [`os_log` docs](https://developer.apple.com/documentation/os/logging);
+for an in-depth look,
+check out
+["Unified Logging and Activity Tracing"](https://developer.apple.com/videos/play/wwdc2016/721/)
+from WWDC 2016
+and
+["Measuring Performance Using Logging"](https://developer.apple.com/videos/play/wwdc2018/405/)
+from WWDC 2018.
+
+{% endwarning %}
+
+* * *
+
+As always,
+an example would be helpful in guiding our discussion.
+In the spirit of transparency and nostalgia,
+let's imagine writing a Swift program
+that audits the finances of a '00s Fortune 500 company.
+
+```swift
+import Foundation
+
+struct Auditor {
+ func watch(_ directory: URL) throws { <#...#> }
+ func cleanup() { <#...#> }
+}
+
+do {
+ let auditor = Auditor()
+
+ defer { auditor.cleanup() }
+ try auditor.watch(directory: URL(string: "ftp://<#...#>/reports")!,
+ extensions: ["xls", "ods", "qdf"]) // poll for changes
+} catch {
+ print("error: \(error)")
+}
+```
+
+An `Auditor` type polls for changes to a directory
+_(an FTP server, because remember: it's 2003)_.
+Each time a file is added, removed, or changed,
+its contents are audited for discrepancies.
+If any financial oddities are encountered,
+they're logged using the `print` function.
+The same goes for issues connecting to the FTP,
+or any other problems the program might encounter —
+everything's logged using `print`.
+
+
+
+Simple enough.
+We can run it from the command line like so:
+
+```terminal
+$ swift run audit
+starting up...
+ERROR: unable to reconnect to FTP
+
+# (try again after restarting PC under our desk)
+
+$ swift run audit
++ connected to FTP server
+! accounting discrepancy in balance sheet
+** Quicken database corruption! **
+^C
+shutting down...
+```
+
+Such a program might be technically compliant,
+but it leaves a lot of room for improvement:
+
+- For one,
+ our output doesn't have any timestamps associated with it.
+ There's no way to know whether a problem was detected an hour ago or last week.
+- Another problem is that our output lacks any coherent structure.
+ At a glance,
+ there's no straightforward way to isolate program noise from real issues.
+- Finally, —
+ _and this is mostly due to an under-specified example_ —
+ it's unclear how this output is handled.
+ Where is this output going?
+ How is it collected, aggregated, and analyzed?
+
+* * *
+
+The good news is that
+all of these problems (and many others) can be solved
+by adopting a formal logging infrastructure in your project.
+
+* * *
+
+## Adopting SwiftLog in Your Swift Program
+
+Adding `SwiftLog` to an existing Swift package is a breeze.
+You can incorporate it incrementally
+without making any fundamental changes to your code
+and have it working in a matter of minutes.
+
+### Add swift-log as a Package Dependency
+
+In your `Package.swift` manifest,
+add `swift-log` as a package dependency and
+add the `Logging` module to your target's list of dependencies.
+
+```swift
+// swift-tools-version:5.1
+
+import PackageDescription
+
+let package = Package(
+ name: "Auditor2000",
+ products: [
+ .executable(name: "audit", targets: ["audit"])
+ ],
+ dependencies: [
+ .package(url: "https://github.com/apple/swift-log.git", from: "1.2.0"),
+ ],
+ targets: [
+ .target(name: "audit", dependencies: ["Logging"])
+ ]
+)
+```
+
+### Create a Shared, Global Logger
+
+`Logger` provides two initializers,
+the simpler of them taking a single `label` parameter:
+
+```swift
+let logger = Logger(label: "com.NSHipster.Auditor2000")
+```
+
+In [POSIX](https://en.wikipedia.org/wiki/POSIX) systems,
+programs operate on three, predefined
+[streams](https://en.wikipedia.org/wiki/Standard_streams):
+
+| File Handle | Description | Name |
+|-------------|-------------|-----------------|
+| 0 | `stdin` | Standard Input |
+| 1 | `stdout` | Standard Output |
+| 2 | `stderr` | Standard Error |
+
+By default,
+`Logger` uses the built-in `StreamLogHandler` type
+to write logged messages to standard output (`stdout`).
+We can override this behavior to instead write to standard error (`stderr`)
+by using the more complex initializer,
+which takes a `factory` parameter:
+a closure that takes a single `String` parameter (the label)
+and returns an object conforming to `LogHandler`.
+
+```swift
+let logger = Logger(label: "com.NSHipster.Auditor2000",
+ factory: StreamLogHandler.standardError)
+```
+
+{% info %}
+
+Alternatively,
+you can set default logger globally
+using the `LoggingSystem.bootstrap()` method.
+
+```swift
+LoggingSystem.bootstrap(StreamLogHandler.standardError)
+let logger = Logger(label: "com.NSHipster.Auditor2000")
+```
+
+After doing this,
+any subsequent `Logger` instances created
+using the `Logger(label:)` initializer
+will default to the specified handler.
+
+{% endinfo %}
+
+### Replacing Print Statements with Logging Statements
+
+Declaring our `logger` as a top-level constant
+lets us call it anywhere within our module.
+Let's revisit our example and spruce it up with our new logger:
+
+```swift
+do {
+ let auditor = Auditor()
+
+ defer {
+ logger.trace("Shutting down")
+ auditor.cleanup()
+ }
+
+ logger.trace("Starting up")
+ try auditor.watch(directory: URL(string: "ftp://<#...#>/reports")!,
+ extensions: ["xls", "ods", "qdf"]) // poll for changes
+} catch {
+ logger.critical("\(error)")
+}
+```
+
+The `trace`, `debug`, and `critical` methods
+log a message at their respective log level.
+`SwiftLog` defines seven levels,
+ranked in ascending order of severity from `trace` to `critical`:
+
+{::nomarkdown}
+
+
+
+
Level
+
Description
+
+
+
+
+
.trace
+
Appropriate for messages that contain information only when debugging a program.
+
+
+
.debug
+
Appropriate for messages that contain information normally of use only when debugging a program.
+
+
+
.info
+
Appropriate for informational messages.
+
+
+
.notice
+
Appropriate for conditions that are not error conditions, but that may require special handling.
+
+
+
.warning
+
Appropriate for messages that are not error conditions, but more severe than .notice
+
+
+
.error
+
Appropriate for error conditions.
+
+
+
.critical
+
Appropriate for critical error conditions that usually require immediate attention.
+
+
+
+{:/}
+
+If we re-run our `audit` example with our new logging framework in place,
+we can see the immediate benefit of clearly-labeled, distinct severity levels
+in log lines:
+
+```terminal
+$ swift run audit
+2020-03-26T09:40:10-0700 critical: Couldn't connect to ftp://<#...#>
+
+# (try again after plugging in loose ethernet cord)
+
+$ swift run audit
+2020-03-26T10:21:22-0700 warning: Discrepancy in balance sheet
+2020-03-26T10:21:22-0700 error: Quicken database corruption
+^C
+```
+
+Beyond merely labeling messages,
+_which — don't get us wrong — is sufficient benefit on its own_,
+log levels provide a configurable level of disclosure.
+Notice that the messages logged with the `trace` method
+don't appear in the example output.
+That's because `Logger` defaults to showing only messages
+logged as `info` level or higher.
+
+You can configure that by setting the `Logger`'s `logLevel` property.
+
+```swift
+var logger = Logger(label: "com.NSHipster.Auditor2000")
+logger.logLevel = .trace
+```
+
+After making this change,
+the example output would instead look something like this:
+
+```terminal
+$ swift run audit
+2020-03-25T09:40:00-0700 trace: Starting up
+2020-03-26T09:40:10-0700 critical: Couldn't connect to ftp://<#...#>
+2020-03-25T09:40:11-0700 trace: Shutting down
+
+# (try again after plugging in loose ethernet cord)
+
+$ swift run audit
+2020-03-25T09:41:00-0700 trace: Starting up
+2020-03-26T09:41:01-0700 debug: Connected to ftp://<#...#>/reports
+2020-03-26T09:41:01-0700 debug: Watching file extensions ["xls", "ods", "qdf"]
+2020-03-26T10:21:22-0700 warning: Discrepancy in balance sheet
+2020-03-26T10:21:22-0700 error: Quicken database corruption
+^C
+2020-03-26T10:30:00-0700 trace: Shutting down
+```
+
+## Using Multiple Logging Handlers at Once
+
+Thinking back to our objections in the original example,
+the only remaining concern
+is what we actually _do_ with these logs.
+
+According to [12 Factor App](https://12factor.net/logs) principles:
+
+> ## XI. Logs
+>
+> _[...]_
+>
+> **A twelve-factor app never concerns itself with
+> routing or storage of its output stream.**
+> It should not attempt to write to or manage logfiles.
+> Instead, each running process writes its event stream, unbuffered, to `stdout`.
+
+Collecting, routing, indexing, and analyzing logs across a distributed system
+often requires a constellation of open-source libraries and commercial products.
+Fortunately,
+most of these components traffic in a shared currency of
+[syslog](https://en.wikipedia.org/wiki/Syslog) messages —
+and thanks to
+[this package by Ian Partridge](https://github.com/ianpartridge/swift-log-syslog),
+Swift can, as well.
+
+That said,
+few engineers have managed to retrieve this information
+from the likes of [Splunk](https://www.splunk.com)
+and lived to tell the tale.
+For us mere mortals,
+we might prefer
+[this package by Will Lisac](https://github.com/wlisac/swift-log-slack),
+which sends log messages to
+[Slack](https://slack.com).
+
+The good news is that we can use both at once,
+without changing how messages are logged at the call site
+by using another piece of the `Logging` module:
+`MultiplexLogHandler`.
+
+```swift
+import struct Foundation.ProcessInfo
+import Logging
+import LoggingSyslog
+import LoggingSlack
+
+LoggingSystem.bootstrap { label in
+ let webhookURL = URL(string:
+ ProcessInfo.processInfo.environment["SLACK_LOGGING_WEBHOOK_URL"]!
+ )!
+ var slackHandler = SlackLogHandler(label: label, webhookURL: webhookURL)
+ slackHandler.logLevel = .critical
+
+ let syslogHandler = SyslogLogHandler(label: label)
+
+ return MultiplexLogHandler([
+ syslogHandler,
+ slackHandler
+ ])
+}
+
+let logger = Logger(label: "com.NSHipster.Auditor2000")
+```
+
+
+
+With all of this in place,
+our system will log everything in syslog format to standard out (`stdout`),
+where it can be collected and analyzed by some other system.
+
+* * *
+
+But the real strength of this approach to logging
+is that it can be extended to meet the specific needs of any environment.
+Instead of writing syslog to `stdout` or Slack messages,
+your system could send emails,
+open SalesForce tickets,
+or trigger a webhook to activate some
+IoT device.
+
+
+
+Here's how you can extend `SwiftLog` to fit your needs
+by writing a custom log handler:
+
+## Creating a Custom Log Handler
+
+The `LogHandler` protocol specifies the requirements for types
+that can be registered as message handlers by `Logger`:
+
+```swift
+protocol LogHandler {
+ subscript(metadataKey _: String) -> Logger.Metadata.Value? { get set }
+ var metadata: Logger.Metadata { get set }
+
+ var logLevel: Logger.Level { get set }
+
+ func log(level: Logger.Level,
+ message: Logger.Message,
+ metadata: Logger.Metadata?,
+ file: String, function: String, line: UInt)
+}
+```
+
+In the process of writing this article,
+I created [custom handler](https://github.com/NSHipster/swift-log-github-actions)
+that formats log messages for GitHub Actions
+so that they're surfaced on GitHub's UI like so:
+
+{% asset github-actions-ui.png %}
+
+{% info %}
+
+For more information,
+see
+["Workflow commands for GitHub Actions."](https://help.github.com/en/actions/reference/workflow-commands-for-github-actions)
+
+{% endinfo %}
+
+If you're interested in making your own logging handler,
+you can learn a lot by just browsing
+[the code for this project](https://github.com/NSHipster/swift-log-github-actions).
+But I did want to call out a few points of interest here:
+
+### Conditional Boostrapping
+
+When bootstrapping your logging system,
+you can define some logic for how things are configured.
+For logging formatters specific to a particular CI vendor,
+for example,
+you might check the environment to see if you're running locally or on CI
+and adjust accordingly.
+
+```swift
+import Logging
+import LoggingGitHubActions
+import struct Foundation.ProcessInfo
+
+LoggingSystem.bootstrap { label in
+ // Are we running in a GitHub Actions workflow?
+ if ProcessInfo.processInfo.environment["GITHUB_ACTIONS"] == "true" {
+ return GitHubActionsLogHandler.standardOutput(label: label)
+ } else {
+ return StreamLogHandler.standardOutput(label: label)
+ }
+}
+```
+
+### Testing Custom Log Handlers
+
+Testing turned out to be more of a challenge than originally anticipated.
+I could be missing something obvious,
+but there doesn't seem to be a way to create assertions about
+text written to standard output.
+So here's what I did instead:
+
+First,
+create an `internal` initializer that takes a `TextOutputStream` parameter,
+and store it in a `private` property.
+
+
+
+```swift
+public struct GitHubActionsLogHandler: LogHandler {
+ private var outputStream: TextOutputStream
+
+ internal init(outputStream: TextOutputStream) {
+ self.outputStream = outputStream
+ }
+
+ <#...#>
+}
+```
+
+Then,
+in the test target,
+create a type that adopts `TextOutputStream`
+and collects logged messages to a stored property
+for later inspection.
+By using a
+[`@testable import`](https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html#ID5)
+of the module declaring `GitHubActionsLogHandler`,
+we can access that `internal` initializer from before,
+and pass an instance of `MockTextOutputStream` to intercept logged messages.
+
+```swift
+import Logging
+@testable import LoggingGitHubActions
+
+final class MockTextOutputStream: TextOutputStream {
+ public private(set) var lines: [String] = []
+
+ public init(_ body: (Logger) -> Void) {
+ let logger = Logger(label: #file) { label in
+ GitHubActionsLogHandler(outputStream: self)
+ }
+
+ body(logger)
+ }
+
+ // MARK: - TextOutputStream
+
+ func write(_ string: String) {
+ lines.append(string)
+ }
+}
+```
+
+With these pieces in place,
+we can finally test that our handler works as expected:
+
+```swift
+func testLogging() {
+ var logLevel: Logger.Level?
+ let expectation = MockTextOutputStream { logger in
+ logLevel = logger.handler.logLevel
+
+ logger.trace("🥱")
+ logger.error("😱")
+ }
+
+ XCTAssertGreaterThan(logLevel!, .trace)
+ XCTAssertEqual(expectation.lines.count, 1) // trace log is ignored
+ XCTAssertTrue(expectation.lines[0].hasPrefix("::error "))
+ XCTAssertTrue(expectation.lines[0].hasSuffix("::😱"))
+}
+```
+
+{% warning %}
+
+As to how or where messages are logged,
+`SwiftLog` is surprisingly tight-lipped.
+There's [an internal type](https://github.com/apple/swift-log/blob/0e21dd789300fc37a43019fba68b4e97c9938142/Sources/Logging/Logging.swift#L524-L550) that buffers writes to `stdout`,
+but it's not exposed by the module.
+
+If you're in search for a replacement
+and would prefer not to copy-paste something as involved as that,
+here's a dead-simple alternative:
+
+```swift
+struct StandardTextOutputStream: TextOutputStream {
+ mutating func write(_ string: String) {
+ print(string)
+ }
+}
+```
+
+{% endwarning %}
+
+
+{% asset articles/swift-log.css %}
+
+
+[redirect]: https://en.wikipedia.org/wiki/Redirection_(computing)
diff --git a/2020-04-13-contact-tracing.md b/2020-04-13-contact-tracing.md
new file mode 100644
index 00000000..dad54576
--- /dev/null
+++ b/2020-04-13-contact-tracing.md
@@ -0,0 +1,516 @@
+---
+title: Contact Tracing
+author: Mattt
+category: Miscellaneous
+excerpt: >-
+ Apple and Google announced a joint initiative
+ to deploy contact tracing functionality
+ to the billions of devices running iOS or Android in the coming months.
+ In this article,
+ we’ll take a first look at these specifications —
+ particularly, Apple’s proposed ExposureNotification framework —
+ in an effort to anticipate what this will all look like in practice.
+status:
+ swift: 5.2
+revisions:
+ "2020-04-13": First Publication
+ "2020-04-29": Updated for Exposure Notification v1.2
+---
+
+> An ounce of prevention is worth a pound of cure.
+
+Early intervention is among the most effective strategies for treating illnesses.
+This is true not only for the human body, for society as a whole.
+That's why public health officials use contact tracing
+as their first line of defense against
+the spread of infectious disease in a population.
+
+We're hearing a lot about contact tracing these days,
+but the technique has been used for decades.
+What's changed is that
+thanks to the ubiquity of personal electronic devices,
+we can automate what was — up until now — a labor-intensive, manual process.
+Much like how "computer" used to be a job title held by humans,
+the role of "contact tracer" may soon be filled primarily by apps.
+
+On April 10th,
+Apple and Google [announced][apple press release] a joint initiative
+to deploy contact tracing functionality
+to the billions of devices running iOS or Android
+in the coming months.
+As part of this announcement,
+the companies shared draft specifications for the
+[cryptography][cryptography specification],
+[hardware][hardware specification],
+and
+[software][software specification]
+involved in their proposed solution.
+
+In this article,
+we'll take a first look at these specifications —
+particularly Apple's proposed `ExposureNotification` framework —
+and use what we've learned to anticipate what
+this will all look like in practice.
+
+{% warning %}
+
+On April 29th,
+Apple released iOS 13.5 beta 1,
+which includes the first public release of the
+`ExposureNotification` (previously `ContactTracing`) framework.
+The content in this article has been updated to reflect these changes.
+
+{% endwarning %}
+
+* * *
+
+## What is contact tracing?
+
+Contact tracing is a technique used by public health officials
+to identify people who are exposed to an infectious disease
+in order to slow the spread of that illness within a population.
+
+When a patient is admitted to a hospital
+and diagnosed with a new, communicable disease,
+they're interviewed by health workers
+to learn who they've interacted recently.
+Any contacts whose interactions with the patient are then evaluated,
+and if they're diagnosed with the disease,
+the process repeats with their known, recent contacts.
+
+Contact tracing disrupts the chain of transmission.
+It gives people the opportunity to isolate themselves before infecting others
+and to seek treatment before they present symptoms.
+It also allows decision-makers to make more informed
+recommendations and policy decisions about additional measures to take.
+
+If you start early and act quickly,
+contact tracing gives you a fighting chance of containing an outbreak
+before it gets out of hand.
+
+Unfortunately, we weren't so lucky this time around.
+
+With over a million confirmed cases of COVID-19 worldwide,
+many regions are well past the point where contact tracing is practical.
+But that's not to say that it can't play an essential role
+in the coming weeks and months.
+
+## "Only Apple (and Google) can do this."
+
+Since the outbreak,
+various [governments](https://www.pepp-pt.org)
+and [academics](https://github.com/DP-3T/documents)
+have proposed standards for contact tracing.
+But the most significant development so far came yesterday
+with Apple and Google's announcement of a joint initiative.
+
+According to the
+NHS,
+around 60% of adults in a population
+would need to participate in order for digital contact tracing to be effective.
+Researchers from the aforementioned institutions have noted
+that the limits imposed by iOS on 3rd-party apps
+make this level of participation unlikely.
+
+On the one hand,
+it feels weird to congratulate Apple for stepping in
+to solve a problem it created in the first place.
+But we can all agree that this announcement
+is something to celebrate.
+It's no exaggeration to say that
+this wouldn't be possible without their help.
+
+## What are Apple and Google proposing as a solution?
+
+At a high level,
+Apple and Google are proposing a common standard
+for how personal electronic devices (phones, tablets, watches)
+can automate the process of contact tracing.
+
+Instead of health workers chasing down contacts on the phone —
+a process that can take hours, or even days —
+the proposed system could identify every recent contact
+and notify all of them within moments of a confirmed, positive diagnosis.
+
+{% info %}
+
+[This infographic][google presentation]
+from Google's blog post announcing the partnership
+provides a nice explanation of the technologies involved.
+
+{% endinfo %}
+
+Apple's CEO, Tim Cook, promises that
+["Contact tracing can help slow the spread of COVID-19 and can be done without compromising user privacy."](https://twitter.com/tim_cook/status/1248657931433693184).
+The specifications accompanying the announcement
+show how that's possible.
+
+Let's take them in turn,
+starting with
+[cryptography][cryptography specification] (key derivation & rotation),
+followed by
+[hardware][hardware specification] (Bluetooth),
+and
+[software][software specification] (app)
+components.
+
+### Cryptography
+
+When you install an app and open it for the first time,
+the Exposure Notification framework displays
+a dialog requesting permission
+to enable contact tracing on the device.
+
+If the user accepts,
+the framework generates a 32-byte cryptographic random number
+to serve as the device's Tracing Key.
+The Tracing Key is kept secret, never leaving the device.
+
+{% info %}
+
+If the concept of "binary data" seems daunting or meaningless to you,
+it can help to see a few examples of how that information
+can be encoded into a human-readable form.
+
+32 bytes of binary data can be represented by
+44-character-long [Base64-encoded][base64] string
+or a string of 64 [hexadecimal][hexadecimal] digits.
+You can generate these for yourself from the command line
+with the following commands:
+
+```terminal
+$ head -c32 < /dev/urandom | xxd -p -c 64
+211ad682549d92fbb6cd5dc42be5121b22f8864b3a7e93cedb9c43c83332440d
+
+$ head -c32 < /dev/urandom | base64
+2pNDyj5LSr0GGi1IL2VOvsovBwmG4Yp5YYP7leg928Y=
+```
+
+16 bytes of binary data can also be represented in Base64 or hexadecimal,
+but it's more common and convenient to use a
+[UUID][rfc4122].
+
+```terminal
+$ uuidgen
+33F1C4D5-3F1C-4FF0-A05E-A267FAB237CB
+```
+
+{% endinfo %}
+
+Every 24 hours,
+the device takes the Tracing Key and the day number (0, 1, 2, ...)
+and uses
+[HKDF][rfc5869]
+to derive a 16-byte Daily Tracing KeyTemporary Exposure Key.
+These keys stay on the device,
+unless you consent to share them.
+
+Every 15 minutes,
+the device takes the Temporary Exposure Key and
+the number of 10-minute intervals since the beginning of the day (0 – 143),
+and uses
+[HMAC][rfc2104]
+to generate a new 16-byte Rolling Proximity Identifier.
+This identifier is broadcast from the device using
+[Bluetooth LE][ble].
+
+If someone using a contact tracing app gets a positive diagnosis,
+the central health authority requests their Temporary Exposure Keys
+for the period of time that they were contagious.
+If the patient consents,
+those keys are then added to the health authority's database as
+Positive Diagnosis Keys.
+Those keys are shared with other devices
+to determine if they've had any contact over that time period.
+
+{% info %}
+
+The [Contact Tracing Cryptography Specification][cryptography specification]
+is concise, clearly written, and remarkably accessible.
+Anyone for whom the name _[Diffie–Hellman][diffie–hellman]_ even rings a bell
+are encouraged to give it a quick read.
+
+{% endinfo %}
+
+### Hardware
+
+Bluetooth organizes communications between devices
+around the concept of services.
+
+A service describes a set of characteristics for accomplishing a particular task.
+A device may communicate with multiple services
+in the course of its operation.
+Many service definitions are [standardized](https://www.bluetooth.com/specifications/gatt/)
+so that devices that do the same kinds of things communicate in the same way.
+
+For example,
+a wireless heart rate monitor
+that uses Bluetooth to communicate to your phone
+would have a profile containing two services:
+a primary Heart Rate service and
+a secondary Battery service.
+
+Apple and Google's Contact Tracing standard
+defines a new Contact Detection service.
+
+When a contact tracing app is running (either in the foreground or background),
+it acts as a peripheral,
+advertising its support for the Contact Detection service
+to any other device within range.
+The Rolling Proximity Identifier generated every 15 minutes
+is sent in the advertising packet along with the 16-bit service UUID.
+
+Here's some code for doing this from an iOS device using
+the [Core Bluetooth framework][core bluetooth]:
+
+```swift
+import CoreBluetooth
+
+// Contact Detection service UUID
+let serviceUUID = CBUUID(string: "FD6F")
+
+// Rolling Proximity Identifier
+let identifier: Data = <#...#> // 16 bytes
+
+let peripheralManager = CBPeripheralManager()
+
+let advertisementData: [String: Any] = [
+ CBAdvertisementDataServiceUUIDsKey: [serviceUUID]
+ CBAdvertisementDataServiceDataKey: identifier
+]
+
+peripheralManager.startAdvertising(advertisementData)
+```
+
+At the same time that the device broadcasts as a peripheral,
+it's also scanning for other devices' Rolling Proximity Identifiers.
+Again, here's how you might do that on iOS using Core Bluetooth:
+
+
+
+```swift
+let delegate: CBCentralManagerDelegate = <#...#>
+let centralManager = CBCentralManager(delegate: delegate, queue: .main)
+centralManager.scanForPeripherals(withServices: [serviceUUID], options: [:])
+
+extension <#DelegateClass#>: CBCentralManagerDelegate {
+ func centralManager(_ central: CBCentralManager,
+ didDiscover peripheral: CBPeripheral,
+ advertisementData: [String : Any],
+ rssi RSSI: NSNumber)
+ {
+ let identifier = advertisementData[CBAdvertisementDataServiceDataKey] as! Data
+ <#...#>
+ }
+}
+```
+
+Bluetooth is an almost ideal technology for contact tracing.
+It's on every consumer smart phone.
+It operates with low power requirement,
+which lets it run continuously without draining your battery.
+And it _just_ so happens to have a transmission range
+that approximates the physical proximity required
+for the airborne transmission of infectious disease.
+This last quality is what allows contact tracing to be done
+without resorting to location data.
+
+
+
+### Software
+
+Your device stores any Rolling Proximity Identifiers it discovers,
+and periodically checks them against
+a list of Positive Diagnosis Keys sent from the central health authority.
+
+Each Positive Diagnosis Key corresponds to someone else's Temporary Exposure Key.
+We can derive all of the possible Rolling Proximity Identifiers
+that it could advertise over the course of that day
+(using the same HMAC algorithm
+that we used to derive our own Rolling Proximity Identifiers).
+If any matches were found among
+your device's list of Rolling Proximity Identifiers,
+it means that you may have been in contact with an infected individual.
+
+Suffice to say that digital contact tracing is really hard to get right.
+Given the importance of getting it right,
+both in terms of yielding accurate results and preserving privacy,
+Apple and Google are providing SDKs for app developers to use
+for iOS and Android, respectively.
+
+All of the details we discussed about cryptography and Bluetooth
+are managed by the framework.
+The only thing we need to do as developers
+is communicate with the user —
+specifically, requesting their permission to start contact tracing
+and notifying them about a positive diagnosis.
+
+## ExposureNotification
+
+When Apple announced the `ContactTracing` framework on April 10th,
+all we had to go on were some annotated Objective-C headers.
+But as of the first public beta of iOS 13.5,
+we now have [official documentation](https://developer.apple.com/documentation/exposurenotification)
+under its name: `ExposureNotification`.
+
+### Calculating Risk of Exposure
+
+A contact tracing app regularly
+fetches new Positive Diagnosis Keys from the central health authority.
+It then checks those keys
+against the device's Rolling Proximity Identifiers.
+Any matches would indicate a possible risk of exposure.
+
+In the first version of `ContactTracing`,
+all you could learn about a positive match was
+how long you were exposed _(in 5 minute increments)_
+and when contact occurred _(with an unspecified level of precision)_.
+While we might applaud the level of privacy protections here,
+that doesn't offer much in the way of actionable information.
+Depending on the individual,
+a push notification saying
+"You were in exposed for 5–10 minutes sometime 3 days ago"
+could warrant a visit to the hospital
+or elicit no more concern than a missed call.
+
+With `ExposureNotification`,
+you get a lot more information, including:
+
+- Days since last exposure incident
+- Cumulative duration of the exposure (capped at 30 minutes)
+- Minimum Bluetooth signal strength attenuation
+ _(Transmission Power - RSSI)_,
+ which can tell you how close they got
+- Transmission risk,
+ which is an app-definied value that may be based on
+ symptoms, level of diagnosis verification,
+ or other determination from the app or a health authority
+
+For each instance of exposure,
+an [`ENExposureInfo`](https://developer.apple.com/documentation/exposurenotification/enexposureinfo)
+object provides all of the aforementioned information
+plus an overall risk score
+_([from 1 to 8](https://developer.apple.com/documentation/exposurenotification/enrisklevel))_,
+which is calculated from
+[the app's assigned weights for each factor](https://developer.apple.com/documentation/exposurenotification/enexposureconfiguration),
+according to this equation:
+
+
+{% asset contact-tracing-equation.svg width="100%" %}
+
+
+S is a score,
+W is a weighting,
+r is risk,
+d is days since exposure,
+t is duration of exposure,
+ɑ is Bluetooth signal strength attenuation
+
+
+
+Apple provides this example in their [framework documentation PDF](https://covid19-static.cdn-apple.com/applications/covid19/current/static/contact-tracing/pdf/ExposureNotification-FrameworkDocumentationv1.2.pdf):
+
+{% asset contact-tracing-example-equation.png %}
+
+### Managing Permissions and Disclosures
+
+The biggest challenge we found with the original Contact Tracing framework API
+was dealing with all of its completion handlers.
+Most of the functionality was provided through asynchronous APIs;
+without a way to [compose](/optional-throws-result-async-await/) these operations,
+you can easily find yourself nested 4 or 5 closures deep,
+indented to the far side of your editor.
+
+
+
+Fortunately,
+the latest release of Exposure Notification includes a new
+[`ENManager`](https://developer.apple.com/documentation/exposurenotification/enmanager) class,
+which simplifies much of that asynchronous state management.
+
+```swift
+let manager = ENManager()
+manager.activate { error in
+ guard error == nil else { <#...#> }
+
+ manager.setExposureNotificationEnabled(true) { error in
+ guard error == nil else { <#...#> }
+
+ // app is now advertising and monitoring for tracing identifiers
+ }
+}
+```
+
+* * *
+
+## Tracing a path back to normal life
+
+Many of us have been sheltering in place for weeks, if not months.
+Until a vaccine is developed and made widely available,
+this is the most effective strategy we have for stopping the spread of the disease.
+
+But experts are saying that a vaccine
+could be anywhere from 9 to 18 months away.
+_"What will we do until then?"_
+
+At least here in the United States,
+we don't yet have a national plan for getting back to normal,
+so it's hard to say.
+What we do know is that
+it's not going to be easy,
+and it's not going to come all at once.
+
+Once the rate of new infections stabilizes,
+our focus will become containing new outbreaks in communities.
+And to that end,
+technology-backed contact tracing can play a crucial role.
+
+From a technical perspective,
+Apple and Google's proposal gives us every reason to believe that
+we _can_ do contact tracing without compromising privacy.
+However,
+the amount of faith you put into this solution
+depends on how much you trust
+these companies and our governments in the first place.
+
+Personally,
+I remain cautiously optimistic.
+Apple's commitment to privacy has long been one of its greatest assets,
+and it's now more important than ever.
+
+[skip]: #tech-talk "Jump to code discussion"
+[apple press release]: https://www.apple.com/newsroom/2020/04/apple-and-google-partner-on-covid-19-contact-tracing-technology/
+[cryptography specification]: https://covid19-static.cdn-apple.com/applications/covid19/current/static/contact-tracing/pdf/ExposureNotification-CryptographySpecificationv1.2.pdf
+[hardware specification]: https://covid19-static.cdn-apple.com/applications/covid19/current/static/contact-tracing/pdf/ExposureNotification-BluetoothSpecificationv1.2.pdf
+[software specification]: https://covid19-static.cdn-apple.com/applications/covid19/current/static/contact-tracing/pdf/ExposureNotification-FrameworkDocumentationv1.2.pdf
+[diffie–hellman]: https://en.wikipedia.org/wiki/Diffie–Hellman_key_exchange
+[rfc2104]: https://tools.ietf.org/html/rfc2104 "HMAC: Keyed-Hashing for Message Authentication"
+[rfc5869]: https://tools.ietf.org/html/rfc5869 "HMAC-based Extract-and-Expand Key Derivation Function (HKDF)"
+[rfc4122]: https://tools.ietf.org/html/rfc4122 "A Universally Unique IDentifier (UUID) URN Namespace"
+[hexadecimal]: https://en.wikipedia.org/wiki/Hexadecimal#Binary_conversion
+[base64]: https://en.wikipedia.org/wiki/Base64
+[ble]: https://en.wikipedia.org/wiki/Bluetooth_Low_Energy
+[google presentation]: https://blog.google/documents/57/Overview_of_COVID-19_Contact_Tracing_Using_BLE.pdf
+[core bluetooth]: https://developer.apple.com/documentation/corebluetooth
+[android contact tracing]: https://blog.google/documents/55/Android_Contact_Tracing_API.pdf
+[swift interface]: https://github.com/NSHipster/ContactTracing-Framework-Interface/blob/master/ContactTracing.swift
+[swift docs]: https://contact-tracing-documentation.nshipster.com
+[delegate pattern]: https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/DelegatesandDataSources/DelegatesandDataSources.html
+[cllocationmanager]: https://developer.apple.com/documentation/corelocation/cllocationmanager
diff --git a/2020-04-22-cross-pollination.md b/2020-04-22-cross-pollination.md
new file mode 100644
index 00000000..626a3e4a
--- /dev/null
+++ b/2020-04-22-cross-pollination.md
@@ -0,0 +1,272 @@
+---
+title: Cross-Pollination
+author: Mattt
+category: Miscellaneous
+excerpt: >-
+ A brief essay about the flowering of ideas,
+ written for the occasion of Earth Day.
+---
+
+April is the month when apple trees start to bloom
+up here in the Pacific Northwest.
+All across Oregon's Willamette Valley,
+from Portland stretching south to Eugene,
+long-barren branches sprout white, 5-petaled blossoms tinged with pink.
+Any other year,
+our family would be taking weekend trips
+southwest to Sherwood or east towards Hood River
+to visit their orchards.
+
+Like the Fuji and Gala varieties that predominate in this region,
+most apple cultivars are self-unfruitful —
+which is to say that they require cross-pollination
+to produce a good crop consistently.
+
+
+
+
+
+When fertilized by the pollen of Fuji apple blossoms
+(or those of Braeburn, Honey Crisp, or McIntosh varieties),
+a Gala apple tree can yield 20 kilograms of fruit each season.
+Those Gala trees, in return, endow their pollen on the Fuji apple trees
+so that they too may blossom and bear one or two bushels of fruit, each.
+
+## The Dance of the Honey Bee
+
+Appletree pollen is sticky.
+In contrast with the windborne pollen of Alder, Birch, and Ash trees
+(whose allergenic quality gave the Willamette its name,
+meaning _"valley of sickness"_ in the indigenous Kalapuya dialect),
+appletrees rely on insects to transfer pollen —
+particularly the honey bee.
+
+Honey bees eat the pollen of flowers and convert their nectar into honey.
+Some of the pollen sticks to their furry bodies,
+which is inadvertently spread as they move from plant to plant.
+
+When a scout bee encounters a food source,
+she flies back to the hive
+and communicates the location of that food source to male worker bees
+by performing what's called a waggle dance.
+Performed in darkness on the vertical honeycomb surface in the hive,
+she's able to convey the precise location of new food sources to them
+by flying a coffee bean-shaped pattern oriented in the direction of the sun.
+It's an incredible feat,
+made all the more remarkable by the fact that bees are not, individually,
+very intelligent.
+Bees have brains on the order of 1 million neurons,
+compared to the 100 billion neurons of a human brain.
+
+If you move a food source closer and farther away from a hive,
+you can see how the dance changes to convey this new information.
+But move it _just_ past some critical point,
+and the dance becomes something entirely different:
+instead of the waggle dance,
+the bee performs a round dance
+with a totally different cadence and flight path.
+
+
+
+For many years,
+the dance language of the bumblebee eluded all those who studied it.
+That is until
+a mathematician named Barbara Shipman
+made the connection between a bee's dance language
+and the six-dimensional geometry of flag manifolds,
+of all things.
+What was the unique insight that allowed her to see what others couldn't?
+She grew up in a family of beekeepers
+and cultivated an interest in mathematics and biology
+that carried throughout her studies.
+
+The leap from furry, buzzing insects to abstract geometry is inconceivable
+unless you're accustomed to looking at the world in that particular way.
+
+## The Rose that Grows From the Dunghill
+
+When Apple first announced the Swift programming language in 2014,
+it generated a flurry of excitement as we all tried to understand it.
+One of the most powerful tools at our disposal for understanding is _analogy_:
+
+`<# New Thing #>` is like `<# Familiar Thing #>` crossed with `<# Another Thing #>`.
+
+So in those early days,
+there was a lot of discussion within the community
+attempting to compare and contrast Swift with
+[Haskell][haskell] or [Go][go] or [Python][python] or [Scheme][scheme] or [Dylan][dylan].
+
+
+
+Last year,
+we saw something similar with at [WWDC 2019](/wwdc-2019/).
+Anyone familiar with [React][react] or [Elm][elm]
+immediately recognized their influence on
+[SwiftUI][swiftui] and [Combine][combine]
+(even if Apple hadn't come out and acknowledged it explicitly).
+
+For some,
+the connection between React and Elm with JavaScript
+is an inconvenient truth.
+I've seen numerous developers profess their disdain for the language
+in ways that echo the old rivalry between iOS and Android
+(or the even older rivalry between [Mac and PC][get a mac]).
+
+And yet,
+there are countless examples of good ideas from _"them"_
+being criticized and mocked until they're incorporated into an Apple product:
+
+- ⌥⇥ app switching on Windows
+ (⌘⇥ on macOS)
+- Dark mode in Android
+ ([added in iOS 13](/dark-mode/))
+- Generics in Java and other languages
+ (a core feature in Swift,
+ [later added to Objective-C][objective-c lightweight generics])
+- [JSX][jsx]-style DSLs
+ declarative UI
+ ([function builders][function builders] in SwiftUI)
+
+All of which begs the question:
+
+_Why did we consider these good ideas heretical until Apple did it?_
+
+## Us vs. Them
+
+Another flavor of this arises from the dichotomy between "Native" and "Hybrid".
+
+
+
+Whenever a company writes some blog post about React Native,
+what inevitably follows is chorus of developers who either
+praise the decision as courageous (if switching away)
+or call it idiotic (if adopting it).
+
+As developers,
+we tend to align ourselves with enlightenment ideals like objectivity.
+We say that we make decisions based in the indisputable reality of fact.
+We consider ourselves reasonable and our decisions well-reasoned.
+
+But to what extent is this actually true?
+Do our thoughts lead us to our preferences,
+or do we use thoughts to rationalize them after the fact?
+
+* * *
+
+In the 1960s and 70s,
+the social psychologist Henri Tajfel and his colleagues
+ran a [series of experiments][minimal groups]
+that demonstrated how little it takes
+for people to engage in intergroup discrimination.
+
+In one experiment,
+a group of boys were shown pictures with clusters of dots
+and instructed to guess how many there were
+as a test of their visual judgment.
+The researchers split the group between
+those who overestimated or underestimated the number.
+Except, they only pretended to do this —
+the boys were, in fact, randomly assigned to one of the two groups.
+They were then given the task of allocating a fixed amount of real money
+to other boys in the study.
+
+The results surprised even the researchers:
+
+Overwhelmingly, the boys chose outcomes where their assigned group
+(under- or over-estimators) received more money than their counterparts —
+_even when that meant getting less overall_.
+
+Successful replication of these results in follow-up studies since then
+presents compelling evidence of this peculiarity in human nature.
+That a willingness to engage in "us vs. them" discrimination
+can arise from completely artificial distinctions,
+irrespective of any rationale of self-interest.
+
+* * *
+
+_How else could you explain the intense tribalism
+around how we talk to computers?_
+
+## The Dream of Purity
+
+When a developer proudly declares something to be
+"Pure Swift" or "100% JavaScript free",
+what are they really saying?
+What's presented as an objective statement of fact
+often feels more like an oath of allegiance.
+
+If you see the existence of competing technologies
+as a fight between good and evil,
+perhaps there are more important battles to fight.
+If you can't evaluate solutions as a series of trade-offs,
+what chance do you have at accomplishing anything at all?
+
+Yes,
+there are real differences between technologies
+and reasonable people disagree about
+which one is best-suited to solve a particular problem.
+But don't mistake this for a moral contest.
+
+> Purity is an ideal;
+> a vision of the condition which needs yet to be created,
+> or such as needs to be diligently protected against the genuine or imagined odds.
+> Without such a vision, neither the concept of purity makes sense,
+> nor the distinction between purity and impurity can be sensibly drawn.
+>
+> – Zygmunt Bauman
+
+* * *
+
+It's of no practical consequence that
+the grounds on which Apple Park sits today
+were fruit orchards a hundred years ago.
+But it's poetic.
+Long before it was "Silicon Valley",
+the stretch of land between the San Andreas and Hayward faults
+was called "the Valley of Heart's Delight"
+for all of its fruit trees and flowering plants.
+
+Dwelling on this,
+you might reflect on how humans are like apple trees.
+That we need a variety of different influences to reach our potential.
+(Even self-starters benefit from a unique perspective).
+
+You might then consider what we share in common with
+the bees that pollinate apple trees.
+Like them,
+our success comes not from our individual intelligence,
+but in our ability to share information.
+
+Whether we're like bees or like apples,
+we come away learning the same lesson:
+We can achieve remarkable results by working together.
+
+[haskell]: https://en.wikipedia.org/wiki/Haskell_(programming_language)
+[go]: https://en.wikipedia.org/wiki/Go_(programming_language)
+[python]: https://en.wikipedia.org/wiki/Python_(programming_language)
+[scheme]: https://en.wikipedia.org/wiki/Scheme_(programming_language)
+[dylan]: https://en.wikipedia.org/wiki/Dylan_(programming_language)
+[react]: https://en.wikipedia.org/wiki/React_(web_framework)
+[elm]: https://en.wikipedia.org/wiki/Elm_(programming_language)
+[swiftui]: https://developer.apple.com/xcode/swiftui/
+[combine]: https://developer.apple.com/documentation/combine
+[get a mac]: https://en.wikipedia.org/wiki/Get_a_Mac
+[function builders]: https://forums.swift.org/t/function-builders/25167
+[objective-c lightweight generics]: https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/using_imported_lightweight_generics_in_swift
+[jsx]: https://reactjs.org/docs/introducing-jsx.html
+[minimal groups]: https://en.wikipedia.org/wiki/Minimal_group_paradigm
diff --git a/2020-06-26-wwdc-2020.md b/2020-06-26-wwdc-2020.md
new file mode 100644
index 00000000..805a7ffb
--- /dev/null
+++ b/2020-06-26-wwdc-2020.md
@@ -0,0 +1,248 @@
+---
+title: WWDC 2020
+author: Mattt
+category: Miscellaneous
+excerpt: >-
+ A brief remark about the excitement of Apple's annual developer conference.
+---
+
+Like everything else in 2020,
+this year's WWDC had to be a little different
+if it was going to happen at all.
+
+When Apple first announced that the conference would be fully remote,
+nobody knew what that would look like, exactly.
+What parts of the dubdub experience would be kept in this new format?
+What details would be lost in translation?
+Could they actually pull it off?
+
+{% info %}
+
+For context:
+Apple live-streamed its first keynote in 2013,
+and technical sessions in 2015.
+Attendees of [WWDC 1997][wwdc 1997] could obtain a collection of CDs
+with video recordings of sessions —
+a far cry from the VHS tape for [WWDC 1990][wwdc 1990].
+
+{% endinfo %}
+
+As it turns out,
+going fully remote wasn't merely good enough —
+it was, in many ways, superior to the original thing.
+There's a lot to like about the new format.
+
+The videos are well-produced,
+and let each presenter's personality really shine.
+Everybody looks and sounds great.
+
+Sessions are tight and well-paced.
+Rather than stretching or cramming content into a fixed time slot,
+they're as long as they need to be.
+And thanks to this more digestible format,
+we're starting to see WWDC clips being shared around,
+which is new and refreshing.
+
+To be honest,
+it's hard to imagine ever going back to a physical conference.
+
+However,
+as someone who had the privilege of attending WWDC in years past,
+there are things I'm going to miss
+(and some that I decidedly won't).
+
+{::nomarkdown type="html"}
+
+
+
+
+
🥰
+
😫
+
+
+
+
+
+
+
Refrigerators stocked with Odwalla smoothies
+
Trying to download the latest Xcode beta over hotel internet
+
+
+
+
+
Lunchtime sessions
+
Eating lunch at or around Moscone
+
+
+
+
WWDC track jackets saving the lives of first-time attendees from the cold of San Francisco summer
+
Being in San Francisco, generally
+
+
+
+
Eating burritos on the terrace of McEnery Convention Center during WWDC check-in
+
Being in San Jose, generally
+
+
+
+
Guessing who would be playing at Bash this year
+
Hearing the same handful of songs on repeat before and after every session
+(this song in particular)
+
+
+
+
+
+
+Watching Apple executives dance at Bash
+
+
+
+
+
+
+
Talking to people as you wait in line for the keynote on Monday morning
+
Waking up late and having to settle for watching from the overflow room
+
+
+
+
Leaving at the end of the week with a mix of hope, fear, and inspiration *
+
Being sick for the next week with dubdub flu
+
+
+{:/}
+
+
+* I'd like to hold on this last point for a moment.
+
+
+
+In the _Before Times_,
+many of us traveled far from home to attend WWDC.
+There was a physical and temporal delineation
+between life before, during, and after the conference.
+Flying out of SFO, SJC, or OAK,
+you escaped Apple's ["reality distortion field"][folklore]
+and its surrounding echo chamber.
+You returned to your normal life.
+
+This year? Not so much.
+
+WWDC 2020 was just another week in this bizarre existence amidst this pandemic.
+Not only is there no escape from the "reality distortion field",
+there isn't even a "normal life" for us to leave or return to.
+
+So here we are,
+filled with anxious excitement and fear;
+our corporeal forms replaced by
+Memoji floating in a black, digital void
+lit only by the screens of our soon-to-be-obsolete MacBooks Pro.
+
+{% asset wwdc-2020-banner.jpg %}
+
+* * *
+
+## Excitement
+
+I don't have a real sense of how everything went over this year.
+There wasn't any applause (or heckling) at this year's keynote
+to gauge the temperature in the room.
+There were no parties to overhear shop talk and hot takes.
+There was no line for lunch
+to make small talk with a fellow attendee.
+
+But if [Twitter][#wwdc20] is anything to go on,
+my overall impression is that everyone is really _excited_.
+
+Which is fine. I get it.
+
+But it should come as no surprise that things announced at WWDC are exciting —
+that's the whole point of having a developer conference in the first place.
+Apple has the best marketing in the world,
+and WWDC is Apple's way of marketing to us.
+
+Here's the thing about excitement:
+It's kryptonite to developers.
+
+Excitement messes with our ability to focus on one thing,
+which is already a big struggle for a lot of us (myself included).
+When you're excited,
+it's almost impossible to get anything done.
+
+There are plenty of voices in the community who are echoing this excitement.
+I can't add anything to that discussion.
+And besides,
+that's not really where my head's at right now.
+
+### Trivial Pursuit
+
+I briefly considered reviving the [NSHipster Quiz](/nshipster-quiz-2/)
+for WWDC 2020,
+but it didn't feel right.
+With everything going on in the world,
+Apple trivia just isn't where my head or heart are right now.
+
+To give you a sense of what I mean, here's what I had for Round 3,
+whose theme was inspired by [Richard Hamming][hamming]:
+
+**Question 1.**
+: What are the important problems of your field?
+
+**Question 2.**
+: What important problems are you working on?
+
+**Question 3.**
+: If what you are doing is not important,
+ why are working on it?
+
+
+
+## Temperance
+
+If you're serious about solving a problem,
+you owe it to yourself to temper any excitement
+that distracts you from making real progress.
+
+In last year's write-up for [WWDC 2019](/wwdc-2019/),
+I concluded with the following,
+as a counterpoint to the conference's theme of `#mindblown` 🤯:
+
+> Taking care of yourself —
+> sleeping enough, eating right, exercising regularly —
+> will do more to improve your productivity
+> than any language or framework out there.
+> Your ability to communicate and collaborate with others
+> will always be a better predictor of success
+> than your choice of technology stack.
+> Your relationships with others
+> are the most significant factors of success and happiness in life.
+
+I stand by this advice, boring as it may be.
+
+It's been an exciting week,
+so take a moment to collect yourself.
+Go on a walk. Take a hike. (Be safe about it.)
+Do whatever you need to break free of the "reality distortion field".
+Once you do, you'll have the necessary distance to determine
+what new technologies you should pay attention to
+and what you can ignore for now.
+
+We have a lot of work ahead of us.
+
+[wwdc 1990]: http://bslabs.net/2020/06/19/wwdc-1990/
+[wwdc 1997]: http://bslabs.net/2018/05/28/wwdc-1997-videos/
+[folklore]: https://www.folklore.org/StoryView.py?story=Reality_Distortion_Field.txt
+[placebo effect]: https://en.wikipedia.org/wiki/Placebo
+[#wwdc20]: https://twitter.com/hashtag/WWDC20
+[hey]: https://www.protocol.com/hey-email-app-store-rejection
+[antitrust]: https://ec.europa.eu/commission/presscorner/detail/en/ip_20_1073
+[developer forums]: https://forums.developer.apple.com
+[hamming]: https://www.cs.virginia.edu/~robins/YouAndYourResearch.html
+[Developer app]: https://mjtsai.com/blog/2020/06/16/apple-developer-app-for-mac/
+[transition]: https://www.apple.com/newsroom/2020/06/apple-announces-mac-transition-to-apple-silicon/
+
+{% asset articles/wwdc-2020.js %}
diff --git a/2020-07-07-as-we-may-code.md b/2020-07-07-as-we-may-code.md
new file mode 100644
index 00000000..e091260e
--- /dev/null
+++ b/2020-07-07-as-we-may-code.md
@@ -0,0 +1,970 @@
+---
+title: As We May Code
+author: Mattt
+category: Miscellaneous
+excerpt: >-
+ What if, instead of lowering source code down for the purpose of execution,
+ we raised it for the purpose of understanding?
+---
+
+Chris Lattner often describes [LLVM] as a process of lowering.
+
+{% asset swift-compilation-diagram.png alt="Swift Compiler Architecture Diagram"%}
+
+You start at the highest level of abstraction,
+source code written in a programming language like Swift or Objective-C.
+That code is parsed into an abstract syntax tree,
+(AST),
+which is progressively transformed into
+lower-level, intermediate representations
+until it finally becomes executable binary.
+
+What if,
+instead of lowering source code down for the purpose of execution,
+
+we _raised_ source code for the purpose of understanding?
+
+
+
+You could say that we already do this to some degree with
+[syntax highlighting](/swiftsyntax/#highlighting-swift-code) \\
+(`func f()` → funcf()),
+[structured editing](/swiftsyntax/), and
+[documentation generation](/swift-documentation/).
+But how far could we take it?
+
+* * *
+
+In this article,
+I'd like to share an idea that I've been kicking around for a while.
+It's something that's come into greater focus with
+my recent work on [`swift-doc`][swift-doc],
+but first started to form during tenure in Apple Developer Publications,
+back in 2015.
+
+The idea is this: \\
+**What if we took the lessons of the [semantic web]
+and applied them to source code?**
+
+Specifically:
+
+- **Representation**:
+ Software components should be represented by
+ a common, language-agnostic data format.
+- **Addressability**:
+ Packages, modules, and their constituent APIs
+ should each have a unique URL identifier.
+- **Decentralization**:
+ Information should be distributed across a federated network of data sources,
+ which can cross-reference one another by URL.
+
+I grew up with the Internet,
+and got to see it, first-hand,
+go from an obscure technology to _the_ dominant cultural force.
+So much of what I see in software development today
+reminds me of what I remember about the web from 20 years ago.
+And if you'll forgive the extended wind-up,
+I think there's a lot we can learn by looking at that evolution.
+
+
+
+{% warning %}
+
+If you're already familiar with semantic web
+or aren't all that interested in a history lesson
+feel free to [skip ahead to the technical details](#skip).
+
+{% endwarning %}
+
+* * *
+
+## Web 1.0 The Web of Documents
+
+Tim Berners-Lee launched the World Wide Web
+from a NeXT workstation 27 years ago.
+His vision for a
+globally-distributed, decentralized network of inter-connected documents
+gave rise to the Internet as we know it today.
+But it was also part of an intellectual tradition dating back to the 1940s,
+which includes
+Vannevar Bush's _[Memex]_,
+Ted Nelson's _[Xanadu]_, and
+Doug Engelbart's _[Mother of All Demos]_.
+
+{% info %}
+
+Dr. Bush coined the term memex in an essay titled
+["As We May Think"][As We May Think],
+published in the July 1945 issue of _The Atlantic_.
+
+{% endinfo %}
+
+In those early days,
+the knowledge being shared was primarily academic.
+As the userbase grew over time,
+so too did the breadth and diversity of the information available.
+And, for a time,
+that's what the Internet was:
+[fan sites](http://www.automaticbeyondbelief.org) for Sunbeam toasters,
+[recipes](http://www.varasanos.com/PizzaRecipe.htm) for Neapolitan-style pizza, and
+[the official website](https://www.spacejam.com) for the 1996 film _Space Jam_.
+
+But the web of documents had limits.
+
+If you wanted to
+shop for appliances,
+see the menu of a pizza shop, or
+get local showtimes for a movie,
+you _might_ be able to do that on the early Internet.
+But you really had to work at it.
+
+Back then,
+you'd start by going to a directory like [Yahoo!] or [DMOZ],
+navigate to the relevant topic,
+and click around until you found a promising lead.
+Most of the time, you wouldn't find what you were looking for;
+instead, you'd disconnect your modem to free up your landline
+and consult the [yellow pages].
+
+{% info %}
+
+As it were,
+the difficulty of finding information on the web
+gave us the term [Sherlocked][sherlocked].
+Apple's v3 release of the eponymous system software
+was widely seen to have killed Karelia Software's
+[Watson](https://en.wikipedia.org/wiki/Karelia_Watson):
+a similar (paid) application for Mac OS X
+that offered a uniform interface to information like stocks, movies, and flights.
+
+{% endinfo %}
+
+This started to change in the early '00s.
+
+## Web 2.0 The Social Web
+
+With [Perl CGI][CGI] and [PHP],
+you could now easily generate web pages on-the-fly.
+This enabled eCommerce and the first commercial uses of the Internet.
+
+After the ensuing [dot-com bubble],
+you had technologies like [Java applets] and [Flash]
+bring a new level of interactivity to web sites.
+Eventually, folks figured out how to use
+[an obscure API from Internet Explorer 5][XMLHttpRequest]
+to replicate this interactivity on normal webpages —
+a technique dubbed [AJAX].
+Interacting with a page and seeing results live, without reloading a page?
+This was _huge_.
+Without that,
+social media might not have taken off as it did.
+
+Anyway,
+the server-side APIs powering those AJAX interactions on the client,
+they were the secret sauce that let the Internet evolve into what it is today.
+
+Remember "[mashups]"?
+
+Thanks to all of these (often unsecured) AJAX endpoints,
+developers could synthesize information across multiple sources
+in ways that nobody had ever thought to do.
+You could get someone's location from [Fire Eagle],
+search for photos taken nearby on [Flickr],
+and use [MOO] to print and deliver prints of them on-demand.
+
+
+
+By the end of the decade,
+the rise of social networks and the early promise of mashups
+started to coalesce into the modern Internet.
+
+## Web 3.0 The Web of Data
+
+The term "Web 3.0" didn't catch on like its predecessor,
+but there's a clear delineation between
+the technologies and culture of the web between the early and late '00s.
+
+It's hard to overstate how much the iPhone's launch in 2007
+totally changed the trajectory of the Internet.
+But many other events played an important role in
+shaping the web as we know it today:
+
+- Google acquiring the company behind [Freebase],
+ giving it a knowledge graph to augment its website index.
+- Facebook launching [Open Graph],
+ which meant everything could now be "Liked"
+ (and everyone could be targeted for advertisements).
+- Yahoo releasing [SearchMonkey] and
+ [BOSS][BOSS],
+ two ambitious (albeit flawed) attempts
+ to carve out a niche from Google's monopoly on search.
+- Wolfram launching [Wolfram|Alpha],
+ which far exceeded what many of us thought was possible
+ for a question answering system.
+
+The Internet always had a lot of information on it;
+the difference now is that
+the information is accessible to machines as well as humans.
+
+Today,
+you can ask Google
+[_"Who was the first person to land on the moon?"_][google query]
+and get an info box saying, _"Commander Neil Armstrong"_.
+You can post a link in Messages
+and see it represented by
+[a rich visual summary](/ios-13/#generate-rich-representations-of-urls)
+instead of a plain text URL.
+You can ask Siri,
+_"What is the [airspeed velocity][@AirspeedSwift] of an unladen swallow?"_ and hear back
+_"I can't get the answer to that on HomePod"_
+_About 25 miles per hour_.
+
+
+
+Think about what we take for granted about the Internet now,
+and try to imagine doing that on the web when it looked
+[like this](https://www.spacejam.com).
+It's hard to think that any of this would be possible without the semantic web.
+
+* * *
+
+## GitHub.com, Present Day The Spider and The Octocat
+
+READMEs on GitHub.com today remind me of
+personal home pages on [Geocities] back in the Web 1.0 days.
+
+{% info %}
+Compare the "build passing" SVG badges found at the top of READMEs in 2020
+to the 80×15px "XHTML 1.1 ✓" badges found at the bottom of websites in 2000.
+
+{% asset as-we-may-code-badges.png width=800 %}
+
+{% endinfo %}
+
+Even with the [standard coat of paint](https://primer.style/css/),
+you see an enormous degree of variance across projects and communities.
+Some are sparse; others are replete with adornment.
+
+And yet,
+no matter what a project’s README looks like,
+onboarding onto a new tool or library entails, well _reading_.
+
+
+
+GitHub offers some structured informational cues:
+language breakdown, license, some metadata about commit activity.
+You can search within the repo using text terms.
+And thanks to [semantic] / [tree-sitter],
+you can even click through to find declarations in some languages.
+
+_But where's a list of methods?_
+_Where are the platform requirements?_ \\
+You have to read the README to find out!
+(Better hope it's up-to-date 😭)
+
+The modest capabilities of browsing and searching code today
+more closely resemble [AltaVista] circa 2000 than Google circa 2020.
+Theres so much more that we could be doing.
+
+* * *
+
+
+
+## RDF Vocabularies The Owl and The Turtle
+
+At the center of the semantic web is something called
+[RDF],
+the Resource Description Framework.
+It's a collection of standards for representing and exchanging data.
+The atomic data entity in RDF
+is called a triple, which comprises:
+
+ - a subject _("the sky")_
+ - a predicate _("has the color")_
+ - an object _("blue"_)
+
+You can organize triples according to a
+vocabulary, or ontology,
+which defines rules about how things are described.
+RDF vocabularies are represented by the
+Web Ontology Language
+([OWL]).
+
+The ideas behind RDF are simple enough.
+Often, the hardest part is navigating
+its confusing, acronym-laden technology stack.
+The important thing to keep in mind is that
+information can be represented in several different ways
+without changing the meaning of that information.
+
+Here's a quick run-down:
+
+[RDF/XML]
+: An XML representation format for RDF graphs.
+
+[JSON-LD]
+: A JSON representation format for RDF graphs.
+
+[N-Triples]
+: A plain text representation format for RDF graphs
+ where each line encodes a subject–predicate–object triple.
+
+[Turtle]
+: A human-friendly, plain text representation format for RDF graphs.
+ A superset of N-Triples,
+ and the syntax used in SPARQL queries.
+
+[SPARQL]
+: A query language for RDF graphs.
+
+### Defining a Vocabulary
+
+Let's start to define a vocabulary for the Swift programming language.
+To start,
+we'll define the concept of a
+`Symbol` along with two subclasses, `Structure` and `Function`.
+We'll also define a `name` property that holds a token (a string)
+that applies to any `Symbol`.
+Finally,
+we'll define a `returns` property that applies to a `Function`
+and holds a reference to another `Symbol`.
+
+```turtle
+@prefix : .
+@prefix owl: .
+@prefix rdf: .
+@prefix rdfs: .
+@prefix xsd: .
+
+:Symbol rdf:type owl:Class .
+
+:name rdf:type owl:FunctionalProperty ;
+ rdfs:domain :Symbol ;
+ rdfs:range xsd:token .
+
+:Structure rdfs:subClassOf :Symbol .
+:Function rdfs:subClassOf :Symbol .
+
+:returns rdf:type owl:FunctionalProperty ;
+ rdfs:domain :Function ;
+ rdfs:range :Symbol .
+```
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+### Parsing Code Declarations
+
+Now consider the following Swift code:
+
+```swift
+struct Widget { <#...#> }
+
+func foo() -> Widget {<#...#>}
+func bar() -> Widget {<#...#>}
+```
+
+We can use [SwiftSyntax] to parse the code into an AST
+and [SwiftSemantics] to convert those AST nodes
+into a more convenient representation.
+
+```swift
+import SwiftSyntax
+import SwiftSemantics
+
+var collector = DeclarationCollector()
+let tree = try SyntaxParser.parse(source: source)
+collector.walk(tree)
+
+collector.functions.first?.name // "foo()"
+collector.functions.first?.returns // "Widget"
+```
+
+Combining this syntactic reading with information from compiler,
+we can express facts about the code in the form of RDF triples.
+
+```json-ld
+{
+ "@context": {
+ "name": {
+ "@id": "http://www.swift.org/#name",
+ "@type": "http://www.w3.org/2001/XMLSchema#token"
+ },
+ "returns": "http://www.swift.org/#returns"
+ },
+ "symbols": [
+ {
+ "@id": "E83C6A28-1E68-406E-8162-D389A04DFB27",
+ "@type": "http://www.swift.org/#Structure",
+ "name": "Widget"
+ },
+ {
+ "@id": "4EAE3E8C-FD96-4664-B7F7-D64D8B75ECEB",
+ "@type": "http://www.swift.org/#Function",
+ "name": "foo()"
+ },
+ {
+ "@id": "2D1F49FE-86DE-4715-BD59-FA70392E41BE",
+ "@type": "http://www.swift.org/#Function",
+ "name": "bar()"
+ }
+ ]
+}
+```
+
+```ntriples
+_:E83C6A28-1E68-406E-8162-D389A04DFB27 .
+_:E83C6A28-1E68-406E-8162-D389A04DFB27 "Widget"^^ .
+_:4EAE3E8C-FD96-4664-B7F7-D64D8B75ECEB .
+_:4EAE3E8C-FD96-4664-B7F7-D64D8B75ECEB "foo()"^^ .
+_:4EAE3E8C-FD96-4664-B7F7-D64D8B75ECEB _:E83C6A28-1E68-406E-8162-D389A04DFB27 .
+_:2D1F49FE-86DE-4715-BD59-FA70392E41BE .
+_:2D1F49FE-86DE-4715-BD59-FA70392E41BE "bar()"^^ .
+_:2D1F49FE-86DE-4715-BD59-FA70392E41BE _:E83C6A28-1E68-406E-8162-D389A04DFB27 .
+```
+
+```turtle
+@prefix swift: .
+@prefix owl: .
+@prefix rdf: .
+@prefix rdfs: .
+@prefix xsd: .
+
+_:Widget rdf:type :Structure ;
+ swift:name "Widget"^^xsd:token .
+
+_:foo rdf:type :Function ;
+ swift:name "foo()"^^xsd:token ;
+ swift:returns _:Widget .
+
+_:bar rdf:type :Function ;
+ swift:name "bar()"^^xsd:token ;
+ swift:returns _:Widget .
+```
+
+Encoding our knowledge into a standard format
+lets anyone access that information — however they like.
+And because these facts are encoded within an ontology,
+they can be validated for coherence and consistency.
+It's totally language agnostic.
+
+### Querying the Results
+
+With an RDF graph of facts,
+we can query it using [SPARQL].
+Or,
+we could load the information into
+a graph database like [Neo4j] or
+a relational database like [PostgreSQL]
+and perform the query in Cypher or SQL, respectively.
+
+```sparql
+PREFIX
+ swift:
+SELECT ?function ?name
+WHERE {
+ ?function a swift:Function ;
+ swift:returns ?type ;
+ swift:name ?name .
+ ?type swift:name "Widget" .
+}
+ORDER BY ?function
+```
+
+```cypher
+MATCH (function:Function)-[:RETURNS]->(symbol:Symbol {name: 'Widget'})
+RETURN function
+```
+
+```sql
+CREATE TABLE symbols (
+ id UUID PRIMARY KEY,
+ name TEXT,
+);
+
+CREATE TABLE functions (
+ returns_id UUID REFERENCES symbols(id),
+) INHERITS (symbols);
+
+--
+
+SELECT f.id, f.name
+FROM functions f
+ INNER JOIN symbols s USING (returns_id);
+WHERE s.name = 'Widget'
+ORDER BY name
+```
+
+Whichever route we take,
+we get the same results:
+
+| id | name |
+|--------------------------------------|--------|
+| 4EAE3E8C-FD96-4664-B7F7-D64D8B75ECEB | foo() |
+| 2D1F49FE-86DE-4715-BD59-FA70392E41BE | bar() |
+
+{% info %}
+
+The semantic web suffers from an admittedly weak software ecosystem.
+While there are dozens of excellent clients for SQL databases,
+you'd be hard-pressed to find much for SPARQL.
+So I was quite pleased to come across
+[this kernel](https://github.com/paulovn/sparql-kernel)
+by Paulo Villegas
+that adds SPARQL support to
+[Jupyter notebooks](https://jupyter.org).
+
+{% endinfo %}
+
+
+### Answering Questions About Your Code
+
+_"What can you do with a knowledge graph?"_
+That's kind of like asking, _"What can you do with Swift?"_
+The answer — _"Pretty much anything"_ — is as true as it is unhelpful.
+
+Perhaps a better framing would be to consider the kinds of questions that
+a knowledge graph of code symbols can help answer:
+
+- Which methods in Foundation produce a `Date` value?
+- Which public types in my project _don't_ conform to `Codable`?
+- Which methods does `Array` inherit default implementations from `RandomAccessCollection`?
+- Which APIs have documentation that includes example code?
+- What are the most important APIs in `MapKit`?
+- Are there any unused APIs in my project?
+- What's the oldest version of iOS that my app could target
+ based on my current API usage?
+- What APIs were added to [Alamofire] between versions 4.0 and 4.2?
+- What APIs in our app are affected by a CVE issued for a 3rd-party dependency?
+
+The possibilities get even more interesting as you layer additional contexts
+by linking Swift APIs to different domains and other programming languages:
+
+- How is this Swift API exposed in Objective-C?
+- Who are the developers maintaining the packages
+ that are pulled in as external dependencies for this project?
+- What's the closest functional equivalent to this Swift package
+ that's written in Rust?
+
+{% info %}
+
+My pitch for a
+[Swift Package Registry Service](https://forums.swift.org/t/swift-package-registry-service/37219/2)
+proposes the use of [JSON-LD] and the
+[Schema.org `SoftwareSourceCode`](https://schema.org/SoftwareSourceCode) vocabulary
+as a standard representation for package metadata,
+which could be easily combined with and cross-referenced against
+semantic representations of code.
+
+{% endinfo %}
+
+## Future Applications The Promise of What Lies Ahead
+
+> Any fact becomes important when it's connected to another.
+>
+> Umberto Eco, _Foucault's Pendulum_
+
+Operating on code symbolically is more powerful
+than treating it as text.
+Once you've experienced proper refactoring tools,
+you'll never want to go back to global find-and-replace.
+
+The leap from symbolic to semantic understanding of code
+promises to be just as powerful.
+What follows are a few examples of potential applications of
+the knowledge graph we've described.
+
+### Flexible Search Queries
+
+GitHub's [advanced search](https://github.com/search/advanced)
+provides an interface to filter results on various
+[facets](https://www.elastic.co/guide/en/app-search/current/facets-guide.html),
+but they're limited to metadata about the projects.
+You can search for Swift code written by
+[`@kateinoigakukun`](https://github.com/kateinoigakukun) in 2020,
+but you can't, for example,
+filter for code compatible with Swift 5.1.
+You can search code for the string "record",
+but you can't disambiguate between type and function definitions
+(`class Record` vs. `func record()`).
+
+As we showed earlier,
+the kinds of queries we can perform across a knowledge graph
+are fundamentally different from what's possible with
+a conventional faceted, full-text search index.
+
+For example,
+here's a SPARQL query to find the urls of repositories
+created by `@kateinoigakukun` and updated this year
+that contain Swift functions named `record`:
+
+```sparql
+PREFIX
+ swift:
+ skos:
+ sdo:
+SELECT ?url
+WHERE {
+ ?function a swift:Function ;
+ swift:name "record" ;
+ skos:member ?repository .
+ ?repository a sdo:SoftwareSourceCode ;
+ sdo:contributor ?contributor;
+ sdo:url ?url ;
+ sdo:dateModified ?date .
+ ?contributor a sdo:Person ;
+ sdo:username "kateinoigakukun" .
+ FILTER (?date >= "2020-01-01")
+}
+ORDER BY ?url
+```
+
+{% info %}
+
+Looking for a more grounded example of semantic code search?
+Check out [Hoogle](https://hoogle.haskell.org):
+a Haskell API search engine
+that lets you search for functions by approximate type signature.
+For instance, you can search for
+[`Ord a => [a] -> [a]`](https://hoogle.haskell.org/?hoogle=Ord%20a%20%3D%3E%20%5Ba%5D%20-%3E%20%5Ba%5D),
+(roughly, `([T]) -> [T] where T: Comparable` in Swift)
+to find various `sort` methods available in the ecosystem.
+
+{% endinfo %}
+
+### Linked Documentation
+
+When faced with
+[missing or incomplete documentation](https://nooverviewavailable.com),
+developers are left to search Google for
+blog posts, tutorials, conference videos, and sample code
+to fill in the gaps.
+Often, this means sifting through pages of irrelevant results —
+to say nothing of outdated and incorrect information.
+
+A knowledge graph can improve search for documentation
+much the same as it can for code,
+but we can go even further.
+Similar to how academic papers contain citations,
+example code can be annotated to include references to
+the canonical APIs it interacts with.
+Strong connections between references and its source material
+make for easy retrieval later on.
+
+Imagine if,
+when you option-click on an API in Xcode
+to get its documentation,
+you also saw a list of sample code and WWDC session videos?
+Or what if we could generate sample code automatically from test cases?
+Wouldn't that be nice?
+
+All of that information is out there,
+just waiting for us to connect the dots.
+
+### Automatic µDependencies
+
+John D. Cook once
+[observed](https://www.johndcook.com/blog/2011/02/03/lego-blocks-and-organ-transplants/),
+code reuse is more like an organ transplant
+than snapping LEGO blocks together.
+Fred Brooks similarly analogized software developers to surgeons in
+[_The Mythical Man-Month_](https://en.wikipedia.org/wiki/The_Mythical_Man-Month).
+
+But that's not to say that things can't get better —
+it'd be hard to argue that they haven't.
+
+Web applications were once described in similar, organic terms,
+but that came to an end with the advent of
+[containerization](https://en.wikipedia.org/wiki/OS-level_virtualization).
+Now you can orchestrate entire multi-cloud deployments automatically
+via declarative configuration files.
+
+Before [CPAN],
+the state of the art for dependency management
+was copy-pasting chunks of code
+[you found on a web page](https://en.wikipedia.org/wiki/Matt%27s_Script_Archive).
+But today, package managers are essential infrastructure for projects.
+
+* * *
+
+What if,
+instead of organizing code into self-contained, modular chunks ourselves,
+we let software do it for us?
+Call it
+FaaD (Functions as a Dependency).
+
+Say you want an implementation of
+[_k_-means clustering](https://en.wikipedia.org/wiki/K-means_clustering).
+You might search around for "k-means" or "clustering" on GitHub
+and find a package named "SwiftyClusterAlgorithms" (😒),
+only to discover that it includes a bunch of functionality that you don't need —
+and to add insult to injury,
+some of those extra bits happen to generate compiler warnings.
+Super annoying.
+
+
+
+Today, there's no automatic way to pick and choose what you need.
+([Swift `import` syntax](/import/) (`import func kMeans`) is a lie)
+But there's no inherent reason why the compiler couldn't do this for you.
+
+Or to go even further:
+If everything compiles down to [web assembly](https://swiftwasm.org),
+there's no inherent requirement for that implementation of _k_-means —
+it could be written in Rust or JavaScript,
+and you'd be none the wiser.
+
+At a certain point,
+you start to question the inherent necessity of software packaging
+as we know it today.
+Take it far enough,
+and you may wonder how much code we'll write ourselves in the future.
+
+### Code Generation
+
+A few months ago,
+Microsoft hosted its [Build](https://mybuild.microsoft.com) conference.
+And among the videos presented was an interview with
+[Sam Altman](https://en.wikipedia.org/wiki/Sam_Altman),
+CEO of [OpenAI](https://openai.com/).
+A few minutes in,
+the interview cut to a video of Sam using
+a fine-tuned version of
+[GPT-2](https://openai.com/blog/gpt-2-1-5b-release/)
+to
+[write Python code from docstrings](https://www.pscp.tv/Microsoft/1OyKAYWPRrWKb?t=27m1s).
+
+```python
+def is_palindrome(s):
+ """Check whether a string is a palindrome"""
+ return s == s[::-1] # ← Generated by AI model from docstring!
+```
+
+And that's using a model that treats code as text.
+Imagine how far you could go with _a priori_ knowledge of programming languages!
+Unlike English, the rules of code are, well, codified.
+You can check to see if code compiles —
+and if it does compile,
+you can run it to see the results.
+
+{% info %}
+
+And that's GPT-2.
+[GPT-3](https://www.gwern.net/GPT-3) is even more impressive.
+For your consideration,
+here's what the model generated
+when prompted to write a parody of the
+[Navy Seal copypasta meme](https://knowyourmeme.com/memes/navy-seal-copypasta)
+relating to
+[mathematical platonism](https://en.wikipedia.org/wiki/Philosophy_of_mathematics#Platonism):
+
+> What in set theory did you just write about me,
+> you ignorant little inductivist?
+> I’ll have you know I am a professor at the University of Chicago
+> and I have been involved in numerous secret raids on the office of Quine,
+> and I have over 300 confirmed set theoreticians. [...]
+
+{% endinfo %}
+
+At this point,
+you should feel either very worried or very excited. \\
+If you don't, then you're not paying attention.
+
+## Taking Ideas Seriously The Shoemaker's Children
+
+> The use of FORTRAN,
+> like the earlier symbolic programming,
+> was very slow to be taken up by the professionals.
+> And this is typical of almost all professional groups.
+> Doctors clearly do not follow the advice they give to others,
+> and they also have a high proportion of drug addicts.
+> Lawyers often do not leave decent wills when they die.
+> Almost all professionals are slow to use their own expertise for their own work.
+> The situation is nicely summarized by the old saying,
+> “The shoe maker’s children go without shoes”.
+> Consider how in the future, when you are a great expert,
+> you will avoid this typical error!
+>
+> Richard W. Hamming, [_"The Art of Doing Science and Engineering"_](https://press.stripe.com/#the-art-of-doing-science-and-engineering)
+
+Today,
+lawyers delegate many paralegal tasks like document discovery to computers
+and
+doctors routinely use machine learning models to help diagnose patients.
+
+So why aren't we —
+_ostensibly the people writing software_ —
+doing more with AI in our day-to-day?
+Why are things like
+[TabNine](https://www.tabnine.com) and
+[Kite](https://kite.com)
+so often seen as curiosities instead of game-changers?
+
+If you take seriously the idea that
+AI
+will fundamentally change the nature of many occupations in the coming decade,
+what reason do you have to believe that you'll be immune from that
+because you work in software?
+Looking at the code you've been paid to write over the past few years,
+how much of that can you honestly say is truly novel?
+
+We're really not as clever as we think we are.
+
+* * *
+
+## Postscript Reflection and Metaprogramming
+
+Today marks 8 years since I started NSHipster.
+
+You might've noticed that I don't write here as much as I once did.
+And on the occasions that I do publish an article,
+it's more likely to include obscure
+[historical facts](/swift-log/) and
+[cultural references](/timeinterval-date-dateinterval/)
+than to the [obscure APIs](/cfbag/) promised by this blog's tagline.
+
+A few weeks out now from [WWDC](/wwdc-2020),
+I _should_ be writing about
+[`DCAppAttestService`](https://developer.apple.com/documentation/devicecheck/dcappattestservice),
+[`SKTestSession`](https://developer.apple.com/documentation/storekittest/sktestsession),
+SwiftUI [`Namespace`](https://developer.apple.com/documentation/swiftui/namespace)
+and
+[`UTType`](https://developer.apple.com/documentation/uniformtypeidentifiers/uttype).
+But here we are,
+at the end of an article about the semantic web, of all things...
+
+* * *
+
+The truth is,
+I've come around to thinking that
+programming isn't the most important thing
+for programmers to pay attention to right now.
+
+* * *
+
+Anyway,
+I'd like to take this opportunity to extend my sincere gratitude
+to everyone who reads the words I write.
+Thank you.
+It may be a while before I get back into a regular cadence,
+so apologies in advance.
+
+Until next time,
+_May your code continue to compile and inspire._
+
+
+[Stack Overflow]: https://stackoverflow.com
+[As We May Think]: https://www.theatlantic.com/magazine/archive/1945/07/as-we-may-think/303881/
+[swift-doc]: https://github.com/SwiftDocOrg/swift-doc/
+[HeaderDoc]: https://en.wikipedia.org/wiki/HeaderDoc
+[WWDC 2001 701]: https://www.youtube.com/watch?v=szgH8p2tkl0
+[no overview available]: https://nooverviewavailable.com
+[Memex]: https://en.wikipedia.org/wiki/Memex
+[Xanadu]: https://en.wikipedia.org/wiki/Project_Xanadu
+[Mother of all Demos]: https://en.wikipedia.org/wiki/The_Mother_of_All_Demos
+[SearchMonkey]: https://en.wikipedia.org/wiki/Yahoo!_SearchMonkey
+[BOSS]: https://en.wikipedia.org/wiki/Yahoo!_Search_BOSS
+[Freebase]: https://en.wikipedia.org/wiki/Freebase_(database)
+[Open Graph]: https://ogp.me
+[Siri]: https://en.wikipedia.org/wiki/Siri
+[Geocities]: https://en.wikipedia.org/wiki/Yahoo!_GeoCities
+[sherlocked]: https://en.wikipedia.org/wiki/Sherlock_(software)#Sherlocked_as_a_term
+[Wolfram|Alpha]: https://www.wolframalpha.com
+[google query]: https://www.google.com/search?q=Who+was+the+first+person+to+land+on+the+moon%3F
+[@AirspeedSwift]: https://twitter.com/AirspeedSwift
+
+[RDF]: https://en.wikipedia.org/wiki/Resource_Description_Framework
+[Triple]: https://en.wikipedia.org/wiki/Semantic_triple
+[RDF/XML]: https://en.wikipedia.org/wiki/RDF/XML
+[N-Triples]: https://en.wikipedia.org/wiki/N-Triples
+[Turtle]: https://en.wikipedia.org/wiki/Turtle_(syntax)
+[JSON-LD]: https://en.wikipedia.org/wiki/JSON-LD
+[SPARQL]: https://en.wikipedia.org/wiki/SPARQL
+[OWL]: https://en.wikipedia.org/wiki/Web_Ontology_Language
+
+[semantic]: https://github.com/github/semantic
+[tree-sitter]: https://github.com/tree-sitter
+[LLVM]: http://llvm.org/
+[strength reduction]: https://en.wikipedia.org/wiki/Strength_reduction
+[constant folding]: https://en.wikipedia.org/wiki/Constant_folding
+
+[Moore's Law]: https://en.wikipedia.org/wiki/Moore%27s_law
+[semantic web]: https://en.wikipedia.org/wiki/Semantic_Web
+[CGI]: https://en.wikipedia.org/wiki/Common_Gateway_Interface
+[CPAN]: https://www.cpan.org/
+[Yahoo!]: https://en.wikipedia.org/wiki/Yahoo!_Directory
+[DMOZ]: https://en.wikipedia.org/wiki/DMOZ
+[yellow pages]: https://en.wikipedia.org/wiki/Yellow_pages
+[PHP]: https://en.wikipedia.org/wiki/PHP
+[dot-com bubble]: https://en.wikipedia.org/wiki/Dot-com_bubble
+[JavaScript]: https://en.wikipedia.org/wiki/JavaScript
+[Flash]: https://en.wikipedia.org/wiki/Adobe_Flash
+[Java applets]: https://en.wikipedia.org/wiki/Java_applet
+[XMLHttpRequest]: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
+[AJAX]: https://en.wikipedia.org/wiki/Ajax_(programming)
+[mashups]: https://en.wikipedia.org/wiki/Mashup_(web_application_hybrid)
+[Flickr]: https://www.flickr.com
+[Fire Eagle]: https://en.wikipedia.org/wiki/Fire_Eagle
+[MOO]: https://www.moo.com/us/
+[Tripod]: https://en.wikipedia.org/wiki/Tripod_(web_hosting)
+[AltaVista]: https://en.wikipedia.org/wiki/AltaVista
+
+[SwiftSyntax]: https://github.com/apple/swift-syntax
+[SwiftSemantics]: https://github.com/SwiftDocOrg/SwiftSemantics
+[Neo4j]: https://neo4j.com
+[PostgreSQL]: https://postgres.app
+[Alamofire]: https://github.com/Alamofire/Alamofire
+
+{% asset articles/as-we-may-code.css %}
diff --git a/2025-01-01-1password-cli.md b/2025-01-01-1password-cli.md
new file mode 100644
index 00000000..490fe6aa
--- /dev/null
+++ b/2025-01-01-1password-cli.md
@@ -0,0 +1,460 @@
+---
+title: op run
+author: Mattt
+category: Miscellaneous
+excerpt: >-
+ `.env` files can create friction in development workflows —
+ especially as teams and projects grow over time.
+ If you're feeling this pain,
+ the 1Password CLI (`op`) might be just what you need.
+status:
+ swift: 6.0
+---
+
+`.env` files.
+If you've worked on a web application,
+you've probably seen one.
+
+While they certainly get the job done,
+`.env` files have shortcomings that can create friction in development workflows.
+
+We've touched on `.env` files in past articles about
+[xcconfig](https://nshipster.com/xcconfig/) files and
+[secret management on iOS](https://nshipster.com/secrets/).
+But this week on NSHipster we're taking a deeper look,
+exploring how the lesser-known
+[1Password CLI](https://developer.1password.com/docs/cli/get-started/) (`op`)
+can solve some problems many of us face managing secrets day-to-day.
+
+---
+
+## The Problem of Configuration
+
+Around 2011, Adam Wiggins published
+["The Twelve-Factor App"](https://12factor.net),
+a methodology for building modern web applications
+that has since become canon in our industry.
+
+The third of those twelve factors,
+["Config"](https://12factor.net/config),
+prescribes storing configuration in environment variables:
+
+> "Apps sometimes store config as constants in the code.
+> This is a violation of twelve-factor,
+> which requires **strict separation of config from code**.
+> Config varies substantially across deploys, code does not."
+
+This core insight — that configuration should be separate from code —
+led to the widespread adoption of `.env` files.
+
+{% info %}
+
+The convention of `.env` files also came out of Heroku at that time,
+by way of David Dollar's [Foreman](https://github.com/ddollar/foreman) tool.
+Brandon Keepers' standalone [dotenv](https://github.com/bkeepers/dotenv) Ruby gem
+came a couple years later.
+Both projects have inspired myriad ports to other languages.
+
+{% endinfo %}
+
+A typical `.env` file looks something like this:
+
+```
+DATABASE_URL=postgres://localhost:5432/myapp_development
+REDIS_URL=redis://localhost:6379/0
+AWS_ACCESS_KEY_ID=AKIA...
+AWS_SECRET_ACCESS_KEY=wJa...
+STRIPE_SECRET_KEY=sk_test_...
+```
+
+You add this file to `.gitignore` to keep it out of version control,
+and load these variables into your environment at runtime with a tool or library.
+
+Simple enough.
+So what's the problem?
+
+### .env Files in Practice
+
+Despite their apparent simplicity,
+`.env` files introduce several points of friction in development workflows:
+
+First, there's the perennial issue of onboarding:
+How does a new team member get what they need to run the app locally?
+The common solution is to have a `.env.sample` / `.env.example` file in version control,
+but this creates a maintenance burden to keep it in sync with the actual requirements.
+And in any case,
+developers still need to go on a scavenger hunt to fill it out
+before they can be productive.
+
+Then there's the multi-environment problem:
+As soon as you need different configurations for development, staging, and production,
+you end up with a proliferation of files:
+`.env.development`, `.env.test`, `.env.staging`...
+Each requiring its own `.sample` / `.example` counterpart.
+
+But perhaps most pernicious is the challenge of managing changes to configuration over time.
+Because `.env` files aren't in version control,
+changes aren't, you know... _tracked anywhere_ 🥲
+
+## Enter the 1Password CLI (`op`)
+
+You may already use [1Password](https://1password.com)
+to manage your passwords and other secrets.
+But what you might not know is that 1Password also has a CLI
+that can integrate directly with your development workflow.
+
+`op` lets you manage 1Password from the command-line.
+You can do all the
+CRUD
+operations you'd expect for items in your vault.
+But its killer features is the `op run` subcommand,
+which can dynamically inject secrets from your 1Password vault
+into your application's environment.
+
+Instead of storing sensitive values directly in your `.env` file,
+you reference them using special `op://` URLs:
+
+```shell
+# .env
+IRC_USERNAME=op://development/chatroom/username
+IRC_PASSWORD=op://development/chatroom/password
+```
+
+
+
+```swift
+import Foundation
+
+guard let username = ProcessInfo.processInfo.environment["IRC_USERNAME"],
+ let password = ProcessInfo.processInfo.environment["IRC_PASSWORD"] else {
+ fatalError("Missing required environment variables")
+}
+
+// For testing only - never print credentials in production code
+print(password)
+```
+
+Run this on its own,
+and you'll fail in proper 12 Factor fashion:
+
+```terminal
+$ swift run
+❗️ "Missing required environment variables"
+```
+
+But by prepending `op run`
+we read in that `.env` file,
+resolve each vault item reference,
+and injects those values into the evironment:
+
+```terminal
+$ op run -- swift run
+hunter2
+```
+
+{% warning %}
+
+The double dash (`--`) after `op run` is important!
+It tells the shell to pass all subsequent arguments to the command being run,
+rather than interpreting them as options to `op run` itself.
+
+{% endwarning %}
+
+You're even prompted to authorize with Touch ID the first time you invoke `op run`.
+
+{::nomarkdown }
+
+
+
+
+
+
+{:/}
+
+---
+
+Ready to give this a test drive?
+Here's how to get started:
+
+## A Step-By-Step Guide to Using the 1Password CLI in .env Files
+
+### Step 1: Install and Configure the 1Password CLI
+
+On macOS, you can install the CLI with [homebrew](https://brew.sh/):
+
+```terminal
+$ brew install 1password-cli
+```
+
+Then, in the 1Password app,
+open Settings (⌘,),
+go to the Developer section,
+and check the box labeled "Integrate with 1Password CLI".
+
+{::nomarkdown }
+
+
+
+
+
+
+{:/}
+
+Running any `op` subcommand should prompt you to connect to the app.
+
+If you get off the happy path,
+consult [the official docs](https://developer.1password.com/docs/cli/get-started/)
+to get back on track.
+
+### Step 2: Create a Shared Vault
+
+Create a new vault in 1Password specifically for development secrets.
+Give it a clear name like "Development" and a useful description.
+
+{::nomarkdown }
+
+
+
+
+
+
+{:/}
+
+### Step 3: Migrate Existing Secrets
+
+For each entry in your `.env` file,
+create a corresponding item in 1Password.
+Choose the appropriate item type:
+
+{::nomarkdown }
+
+{:/}
+
+### Step 4: Update Your .env File
+
+Replace raw values in your `.env` file
+with `op://` references using the following format:
+
+```
+op://<#vault#>/<#item#>/<#field#>
+```
+
+Each reference consists of three components:
+- The vault name (e.g., "development")
+- The item name or UUID
+- The field name from the item
+
+For example, here's how you might reference credentials for various services:
+
+```shell
+# Reference by item name (case-insensitive)
+AWS_ACCESS_KEY_ID=op://development/AWS/username
+AWS_SECRET_ACCESS_KEY=op://development/WorkOS/credential
+
+# Reference by item UUID
+STRIPE_SECRET_KEY=op://development/abc123xyz789defghijklmnop/password
+
+# Using different field names based on item type
+DATABASE_HOST=op://development/db/server
+DATABASE_USER=op://development/db/username
+DATABASE_PASSWORD=op://development/db/password
+DATABASE_NAME=op://development/db/database
+```
+
+{::nomarkdown}
+
+
+
+ You can locate the UUID for any item in 1Password by
+ clicking the "More actions" button (⋮, whatever you want to call that)
+ and selecting "Copy item UUID".
+
+
+ Both item name and UUID references work,
+but using UUIDs can be more reliable in automation contexts
+since they're guaranteed to be unique and won't change if you rename the item.
+
+
+
+
+
+
+
+{:/}
+
+Once you've replaced all sensitive values with `op://` references,
+you can safely commit your `.env` file to version control.
+The references themselves don't contain any sensitive information –
+they're just pointers to your 1Password vault.
+
+{% info %}
+
+You might find that `.env` files are excluded by your global Git configuration.
+To override this, add the following to your repository's `.gitignore`:
+
+```shell
+# Override global .gitignore to allow .env containing op:// references
+!.env
+```
+
+The exclamation point (`!`) [negates a previous pattern](https://git-scm.com/docs/gitignore#_pattern_format),
+allowing you to explicitly include a file that would otherwise be ignored.
+
+{% endinfo %}
+
+### Step 5. Update Your Development Script
+
+Whatever command you normally run to kick off your development server,
+you'll need to prepend `op run --` to that.
+
+For example, if you follow the
+["Scripts to Rule Them All"](https://github.com/github/scripts-to-rule-them-all) pattern,
+you'd update `script/start` like so:
+
+```diff
+#!/bin/sh
+
+- swift run
++ op run -- swift run
+```
+
+{% info %}
+
+`op run` does a neat trick by creating a
+[pseudoterminal (PTY)](https://en.wikipedia.org/wiki/Pseudoterminal) pair
+to redact secrets if printed out directly to `stdout`:
+
+```terminal
+$ op run -- env
+LANG=en_US.UTF-8
+IRC_USERNAME=
+IRC_PASSWORD=
+```
+
+This behavior can be turned off with the `--no-masking` option.
+
+{% endinfo %}
+
+### Advantages Over Traditional .env Files
+
+`op run` solves many of the problems inherent to `.env` files:
+
+- **No More Cold Start Woes**:
+ New team members get access to all required configuration
+ simply by joining the appropriate 1Password vault.
+
+- **Automatic Updates**:
+ When credentials change,
+ they're automatically updated for everyone on the team.
+ No more out-of-sync configuration.
+
+- **Proper Secret Management**:
+ 1Password provides features
+ like access controls, versioning, and integration with
+ [Have I Been Pwned](https://haveibeenpwned.com/).
+
+## Potential Gotchas
+
+Like any technical solution,
+there are some trade-offs to consider:
+
+- **Performance**:
+ `op run` adds a small overhead to command startup time
+ (typically less than a second).
+ 1
+
+- **stdout/stderr Handling**:
+ As mentioned above,
+ `op run` modifies `stdout`/`stderr` to implement secret masking,
+ which can interfere with some terminal applications.
+ 2
+
+- **Dev Container Support**:
+ If you use [VSCode Dev Containers](https://containers.dev/overview),
+ you may encounter some friction with the 1Password CLI.
+ 3
+
+## Driving Technical Change
+
+The implementation is often the easy part.
+The real challenge can be getting your team on board with the change.
+
+First, state the problem you're trying to solve.
+Change for change's sake is rarely helpful.
+
+Next, figure out who you need to get buy-in from.
+Talk to them.
+Articulate specific pain point that everyone recognizes,
+like the frustration of onboarding new team members
+or the time wasted debugging configuration-related issues.
+
+
+Once you've gotten the green light,
+move slowly but deliberately.
+Start small by migrating a single credential,
+or maybe all of the credentials in a smaller project.
+Build up confidence that this approach is a good fit —
+both technically and socially.
+
+---
+
+Managing development secrets is one of those problems
+that seems trivial at first but can become a significant source of friction
+as your team and application grow.
+
+The 1Password CLI offers a more sophisticated approach
+that integrates with tools developers already use and trust.
+
+While it may not be the right solution for every team,
+it's worth considering if you're feeling the pain of traditional `.env` files.
+
+{% asset articles/1password-cli.css @inline %}
diff --git a/2025-02-14-ollama.md b/2025-02-14-ollama.md
new file mode 100644
index 00000000..7bc3118e
--- /dev/null
+++ b/2025-02-14-ollama.md
@@ -0,0 +1,363 @@
+---
+title: Ollama
+author: Mattt
+category: Miscellaneous
+excerpt: >-
+ While we wait for Apple Intelligence to arrive on our devices,
+ something remarkable is already running on our Macs.
+ Think of it as a locavore approach to artificial intelligence:
+ homegrown, sustainable, and available year-round.
+status:
+ swift: 6.0
+---
+
+> "Only Apple can do this"
+> Variously attributed to Tim Cook
+
+Apple introduced [Apple Intelligence](https://www.apple.com/apple-intelligence/) at WWDC 2024.
+After waiting almost a year for Apple to,
+in Craig Federighi's words, _"get it right"_,
+its promise of "AI for the rest of us" feels just as distant as ever.
+
+
+
+While we wait for Apple Intelligence to arrive on our devices,
+something remarkable is already running on our Macs.
+Think of it as a locavore approach to artificial intelligence:
+homegrown, sustainable, and available year-round.
+
+This week on NSHipster,
+we'll look at how you can use Ollama to run
+LLMs locally on your Mac —
+both as an end-user and as a developer.
+
+---
+
+## What is Ollama?
+
+Ollama is the easiest way to run large language models on your Mac.
+You can think of it as "Docker for LLMs" -
+a way to pull, run, and manage AI models as easily as containers.
+
+Download Ollama with [Homebrew](https://brew.sh/)
+or directly from [their website](https://ollama.com/download).
+Then pull and run [llama3.2](https://ollama.com/library/llama3.2) (2GB).
+
+```terminal
+$ brew install --cask ollama
+$ ollama run llama3.2
+>>> Tell me a joke about Swift programming.
+What's a Apple developer's favorite drink?
+The Kool-Aid.
+```
+
+Under the hood,
+Ollama is powered by [llama.cpp](https://github.com/ggerganov/llama.cpp).
+But where llama.cpp provides the engine,
+Ollama gives you a vehicle you'd actually want to drive —
+handling all the complexity of model management, optimization, and inference.
+
+Similar to how Dockerfiles define container images,
+Ollama uses Modelfiles to configure model behavior:
+
+```dockerfile
+{% raw %}
+FROM mistral:latest
+PARAMETER temperature 0.7
+TEMPLATE """
+{{- if .First }}
+You are a helpful assistant.
+{{- end }}
+
+User: {{.Prompt}}
+Assistant: """
+{% endraw %}
+```
+
+Ollama uses the [Open Container Initiative (OCI)](https://opencontainers.org)
+standard to distribute models.
+Each model is split into layers and described by a manifest,
+the same approach used by Docker containers:
+
+```json
+{
+ "mediaType": "application/vnd.oci.image.manifest.v1+json",
+ "config": {
+ "mediaType": "application/vnd.ollama.image.config.v1+json",
+ "digest": "sha256:..."
+ },
+ "layers": [
+ {
+ "mediaType": "application/vnd.ollama.image.layer.v1+json",
+ "digest": "sha256:...",
+ "size": 4019248935
+ }
+ ]
+}
+```
+
+Overall, Ollama's approach is thoughtful and well-engineered.
+And best of all, _it just works_.
+
+## What's the big deal about running models locally?
+
+[Jevons paradox](https://en.wikipedia.org/wiki/Jevons_paradox) states that,
+as something becomes more efficient, we tend to use _more_ of it, not less.
+
+Having AI on your own device changes everything.
+When computation becomes essentially free,
+you start to see intelligence differently.
+
+While frontier models like GPT-4 and Claude are undeniably miraculous,
+there's something to be said for the small miracle of running open models locally.
+
+- **Privacy**:
+ Your data never leaves your device.
+ Essential for working with sensitive information.
+- **Cost**:
+ Run 24/7 without usage meters ticking.
+ No more rationing prompts like '90s cell phone minutes.
+ Just a fixed, up-front cost for unlimited inference.
+- **Latency**:
+ No network round-trips means faster responses.
+ Your `/M\d Mac((Book( Pro| Air)?)|Mini|Studio)/` can easily generate dozens of tokens per second.
+ (Try to keep up!)
+- **Control**:
+ No black-box RLHF or censorship.
+ The AI works for you, not the other way around.
+- **Reliability**:
+ No outages or API quota limits.
+ 100% uptime for your [exocortex](https://en.wiktionary.org/wiki/exocortex).
+ Like having Wikipedia on a thumb drive.
+
+## Building macOS Apps with Ollama
+
+Ollama also exposes an [HTTP API](https://github.com/ollama/ollama/blob/main/docs/api.md) on port 11434
+([leetspeak](https://en.wikipedia.org/wiki/Leet) for llama 🦙).
+This makes it easy to integrate with any programming language or tool.
+
+To that end, we've created the [Ollama Swift package](https://github.com/mattt/ollama-swift)
+to help developers integrate Ollama into their apps.
+
+### Text Completions
+
+The simplest way to use a language model is to generate text from a prompt:
+
+```swift
+import Ollama
+
+let client = Client.default
+let response = try await client.generate(
+ model: "llama3.2",
+ prompt: "Tell me a joke about Swift programming.",
+ options: ["temperature": 0.7]
+)
+print(response.response)
+// How many Apple engineers does it take to document an API?
+// None - that's what WWDC videos are for.
+```
+
+### Chat Completions
+
+For more structured interactions,
+you can use the chat API to maintain a conversation with multiple messages and different roles:
+
+```swift
+let initialResponse = try await client.chat(
+ model: "llama3.2",
+ messages: [
+ .system("You are a helpful assistant."),
+ .user("What city is Apple located in?")
+ ]
+)
+print(initialResponse.message.content)
+// Apple's headquarters, known as the Apple Park campus, is located in Cupertino, California.
+// The company was originally founded in Los Altos, California, and later moved to Cupertino in 1997.
+
+let followUp = try await client.chat(
+ model: "llama3.2",
+ messages: [
+ .system("You are a helpful assistant."),
+ .user("What city is Apple located in?"),
+ .assistant(initialResponse.message.content),
+ .user("Please summarize in a single word")
+ ]
+)
+print(followUp.message.content)
+// Cupertino
+```
+
+### Generating text embeddings
+
+[Embeddings](https://en.wikipedia.org/wiki/Word_embedding)
+convert text into high-dimensional vectors that capture semantic meaning.
+These vectors can be used to find similar content or perform semantic search.
+
+For example, if you wanted to find documents similar to a user's query:
+
+```swift
+let documents: [String] = <#...#>
+
+// Convert text into vectors we can compare for similarity
+let embeddings = try await client.embeddings(
+ model: "nomic-embed-text",
+ texts: documents
+)
+
+/// Finds relevant documents
+func findRelevantDocuments(
+ for query: String,
+ threshold: Float = 0.7, // cutoff for matching, tunable
+ limit: Int = 5
+) async throws -> [String] {
+ // Get embedding for the query
+ let [queryEmbedding] = try await client.embeddings(
+ model: "llama3.2",
+ texts: [query]
+ )
+
+ // See: https://en.wikipedia.org/wiki/Cosine_similarity
+ func cosineSimilarity(_ a: [Float], _ b: [Float]) -> Float {
+ let dotProduct = zip(a, b).map(*).reduce(0, +)
+ let magnitude = { sqrt($0.map { $0 * $0 }.reduce(0, +)) }
+ return dotProduct / (magnitude(a) * magnitude(b))
+ }
+
+ // Find documents above similarity threshold
+ let rankedDocuments = zip(embeddings, documents)
+ .map { embedding, document in
+ (similarity: cosineSimilarity(embedding, queryEmbedding),
+ document: document)
+ }
+ .filter { $0.similarity >= threshold }
+ .sorted { $0.similarity > $1.similarity }
+ .prefix(limit)
+
+ return rankedDocuments.map(\.document)
+}
+```
+
+{% info %}
+For simple use cases,
+you can also use Apple's
+[Natural Language framework](https://developer.apple.com/documentation/naturallanguage/)
+for text embeddings.
+They're fast and don't require additional dependencies.
+
+```swift
+import NaturalLanguage
+
+let embedding = NLEmbedding.wordEmbedding(for: .english)
+let vector = embedding?.vector(for: "swift")
+```
+{% endinfo %}
+
+### Building a RAG System
+
+Embeddings really shine when combined with text generation in a
+RAG (Retrieval Augmented Generation) workflow.
+Instead of asking the model to generate information from its training data,
+we can ground its responses in our own documents by:
+
+1. Converting documents into embeddings
+2. Finding relevant documents based on the query
+3. Using those documents as context for generation
+
+Here's a simple example:
+
+```swift
+let query = "What were AAPL's earnings in Q3 2024?"
+let relevantDocs = try await findRelevantDocuments(query: query)
+let context = """
+ Use the following documents to answer the question.
+ If the answer isn't contained in the documents, say so.
+
+ Documents:
+ \(relevantDocs.joined(separator: "\n---\n"))
+
+ Question: \(query)
+ """
+
+let response = try await client.generate(
+ model: "llama3.2",
+ prompt: context
+)
+```
+
+---
+
+To summarize:
+Different models have different capabilities.
+
+* Models like [llama3.2](https://ollama.com/library/llama3.2)
+ and [deepseek-r1](https://ollama.com/library/deepseek-r1)
+ generate text.
+ * Some text models have "base" or "instruct" variants,
+ suitable for fine-tuning or chat completion, respectively.
+ * Some text models are tuned to support [tool use](https://ollama.com/blog/tool-support),
+ which let them perform more complex tasks and interact with the outside world.
+
+* Models like [llama3.2-vision](https://ollama.com/library/llama3.2-vision)
+ can take images along with text as inputs.
+
+* Models like [nomic-embed-text](https://ollama.com/library/nomic-embed-text)
+ create numerical vectors that capture semantic meaning.
+
+With Ollama,
+you get unlimited access to a wealth of these and many more open-source language models.
+
+---
+
+So, what can you build with all of this?
+Here's just one example:
+
+### Nominate.app
+
+[Nominate](https://github.com/nshipster/nominate)
+is a macOS app that uses Ollama to intelligently rename PDF files based on their contents.
+
+Like many of us striving for a paperless lifestyle,
+you might find yourself scanning documents only to end up with
+cryptically-named PDFs like `Scan2025-02-03_123456.pdf`.
+Nominate solves this by combining AI with traditional NLP techniques
+to automatically generate descriptive filenames based on document contents.
+
+
+
+
+
+The app leverages several technologies we've discussed:
+- Ollama's API for content analysis via the `ollama-swift` package
+- Apple's PDFKit for OCR
+- The Natural Language framework for text processing
+- Foundation's `DateFormatter` for parsing dates
+
+{% info %}
+Nominate performs all processing locally. Your documents never leave your computer. This is a key advantage of running models locally versus using cloud APIs.
+{% endinfo %}
+
+## Looking Ahead
+
+> "The future is already here – it's just not evenly distributed yet."
+> William Gibson
+
+Think about the timelines:
+- Apple Intelligence was announced last year.
+- Swift came out 10 years ago.
+- SwiftUI 6 years ago.
+
+If you wait for Apple to deliver on its promises,
+**you're going to miss out on the most important technological shift in a generation**.
+
+The future is here today.
+You don't have to wait.
+With Ollama, you can start building the next generation of AI-powered apps
+_right now_.
diff --git a/2025-03-07-model-context-protocol.md b/2025-03-07-model-context-protocol.md
new file mode 100644
index 00000000..2121e6cb
--- /dev/null
+++ b/2025-03-07-model-context-protocol.md
@@ -0,0 +1,426 @@
+---
+title: Model Context Protocol (MCP)
+author: Mattt
+category: Miscellaneous
+excerpt: >-
+ Language Server Protocol (LSP)
+ revolutionized how programming languages integrate with developer tools.
+ Model Context Protocol (MCP)
+ aims to do the same for a new generation of AI tools.
+status:
+ swift: 6.0
+---
+
+Language Server Protocol (LSP)
+revolutionized how programming languages integrate with developer tools.
+Model Context Protocol (MCP)
+aims to do the same for a new generation of AI tools.
+
+But before we lay our scene of two protocols (both alike in dignity),
+let's take a moment to motivate the problem a bit more.
+
+## Tool Use & Agents
+
+Today’s frontier models are extraordinary.
+But they’re limited in two key ways:
+
+1. They don’t know anything outside their training data
+2. They can’t do anything outside of predicting the next token
+
+To their credit, LLMs _(kinda, sorta)_ understand these limitations,
+and have been trained to ask for what they need.
+We (humans) have settled on a practice called tool use
+that lets AI do just that.
+
+Here's an example:
+
+
+
+{::nomarkdown}
+
+
User
+
Write a limerick about today's weather in Portland, Oregon.
+
+
Client, to Assistant →
+
+ Also, there's this "Get Weather" tool available if you need it.
+ It takes a set of coordinates and returns the current temperature and conditions.
+
+
+
Assistant
+
+ <thinking>
+ To write an accurate poem, I should get the weather.
+ From my training, I know Portland's coordinates to be
+ 45.5155° N, 122.6789° W
+ </thinking>
+
+ There once was a city called Portland
+ Where clouds made the skies quite importland
+ At twelve degrees C
+ It's as gray as can be
+ With a dampness that's truly absorbland!
+
+
+
+{:/}
+
+Pretty clever, right?
+But then again, so what?
+
+On its face, this is a very slow, expensive way to do something
+better-served by a quick Google search —
+or sticking your head out a window, for that matter.
+
+But here's the thing:
+**Tool use gives rise to agency**
+
+ (✨𝓮𝓶𝓮𝓻𝓰𝓮𝓷𝓬𝓮✨)
+
+
+As the saying goes,
+Quantity has a quality all its own.
+Give a language model a dozen... a hundred... a thousand tools —
+hell, give it _a tool for making more tools_.
+How confident are you that you could find a problem
+that couldn't be solved by such a system?
+
+We're only just starting to see what's possible.
+
+---
+
+But back to more practical matters:
+Let's talk about where we are today with Model Context Protocol.
+
+---
+
+## The New M × N Problem
+
+We've [written previously](https://nshipster.com/language-server-protocol)
+about Language Server Protocol,
+and the M × N problem.
+LSP's challenge was connecting `M` editors with `N` programming languages
+MCP faces a similar challenge, of connecting `M` clients with `N` resources.
+Without MCP,
+each AI application must implement custom integrations
+for every data source it wants to access.
+
+
+
+This creates the same kind of fragmented ecosystem that plagued development tools before LSP:
+- Some AI applications offer deep integration with specific data sources but limited support for others
+- Different applications implement similar integrations in incompatible ways
+- Improvements to one integration rarely benefit the broader ecosystem
+
+Like LSP,
+MCP transforms this M × N problem into an M + N problem through standardization.
+Rather than each AI application implementing custom integrations,
+it only needs to support the MCP standard.
+In doing so,
+it gains access to all MCP-compatible data sources and tools 🌈
+
+
+
+## How Model Context Protocol Works
+
+MCP follows a client-server architecture similar to LSP:
+
+- The *client* is typically an AI application or development environment
+ For example,
+ [Claude Desktop](https://claude.ai/download),
+ [Zed](https://zed.dev), and
+ [Cursor](https://www.cursor.com).
+- The *server* is a program that provides access to data and/or tools
+
+Requests and responses are encoded according to the
+[JSON-RPC](https://www.jsonrpc.org/) 2.0 specification.
+Communication between client and server happens over
+Stdio (`stdin`/`stdout`) or HTTP with Server-Sent Events
+[transports](https://modelcontextprotocol.io/docs/concepts/architecture#transport-layer).
+
+Like LSP, MCP has clients and servers negotiate a set of capabilities.
+When a client connects to a server, it sends an
+[`initialize` message](https://spec.modelcontextprotocol.io/specification/2024-11-05/basic/lifecycle/#initialization),
+with information about what protocol version it supports.
+The server responds in kind.
+
+From there, the client can ask the server about what features it has.
+MCP describes three different kinds of features that a server can provide:
+
+- **Prompts**:
+ Templates that shape how language models respond.
+ They're the difference between getting generic text and precise, useful results.
+ A good prompt is like a well-designed API -
+ it defines the contract between you and the model.
+- **Resources**:
+ Reference materials that ground models in reality.
+ By providing structured data alongside your query,
+ you transform a model from a creative writer into
+ an informed assistant with domain-specific knowledge.
+ _(Think: databases, file systems, documents)_
+- **Tools**:
+ Functions that extend what models can do.
+ They allow AI to calculate, retrieve information,
+ or interact with external systems when simple text generation isn't enough.
+ Tools bridge the gap between language understanding and practical capability.
+
+{% info %}
+
+The distinction between these is admittedly fuzzy.
+After all, all they ultimately do is fill a context window with tokens.
+You could, for example, implement everything as a tool.
+
+
+
+{% endinfo %}
+
+Our previous example handwaved the existence of a "Get Weather" tool.
+MCP gives our client a standard way to consult various connected services.
+
+To get a list of available tools on an MCP,
+the client would send a `tools/list` request to the server:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1,
+ "method": "tools/list",
+ "params": {}
+}
+```
+
+In our example, the server would respond:
+
+```json
+{
+ "jsonrpc":"2.0",
+ "id": 1,
+ "result": {
+ "tools": [
+ {
+ "name": "get_weather",
+ "description": "Returns current weather conditions for the specified coordinates.",
+ "inputSchema": {
+ "type": "object",
+ "properties": {
+ "latitude": { "type": "number" },
+ "longitude": { "type": "number" }
+ },
+ "required": ["latitude", "longitude"]
+ }
+ }
+ ]
+ }
+}
+```
+
+The client can share this list of tools with the language model
+in a system prompt or a user message.
+When the model responds wanting to invoke the `get_weather` tool,
+the client asks the user to confirm tool use.
+If the human-in-the-loop says 🆗,
+the client sends a `tools/call` request:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 2,
+ "method": "tools/call",
+ "params": {
+ "name": "get_weather",
+ "arguments": {
+ "latitude": 45.5155,
+ "longitude": -122.6789
+ }
+ }
+}
+```
+
+In response, the server sends:
+
+```json
+{
+ "jsonrpc":"2.0",
+ "id": 2,
+ "content": [
+ {
+ "type": "text",
+ "text": "{\"temperature\": 12, \"conditions\": \"cloudy\", \"humidity\": 85}"
+ "annotations": {
+ "audience": ["assistant"]
+ }
+ }
+ ]
+}
+```
+
+The client then passes that result to the AI assistant,
+the assistant generates a response with this information,
+and the client passes that along to the user.
+
+---
+
+That's pretty much all there is to it.
+There are plenty of details to get bogged down with.
+But that's what LLMs are for.
+Now is the time for vibes coding.
+MCP is [punk rock](https://www.itsnicethat.com/features/toby-mott-oh-so-pretty-punk-in-print-phaidon-111016).
+
+{% asset model-context-protocol-now-form-a-band.webp %}
+
+## How do I start?
+
+MCP is an emerging standard from Anthropic.
+So it's no surprise that [Claude Desktop](https://claude.ai/download)
+is most capable of showing off what it can do.
+
+Once you have Claude Desktop installed,
+you can peruse the
+[myriad example servers](https://modelcontextprotocol.io/examples) available.
+
+Or, if you want to skip straight to la crème de la crème,
+then have a taste of what we've been cooking up with MCP lately:
+
+### iMCP
+
+Fun fact! The word _"paradise"_ derives from an old word for _"walled garden"_.
+
+
+Ironic how Apple has a way of making your digital life a living hell sometimes.
+
+For many of us who exist in Apple's walled garden,
+we’re often frustrated by the product design and software quality
+that gets between us and our data.
+Spotlight search is stuck in the ‘00s.
+Apple Intelligence [didn’t live up to the hype](https://nshipster.com/ollama).
+Siri seems doomed to suck forever.
+
+That was our motivation for building [iMCP](https://iMCP.app).
+
+{::nomarkdown }
+
+
+
+
+{:/}
+
+iMCP is a macOS app for connecting your digital life with AI.
+It works with Claude Desktop and a growing list of clients that support MCP.
+It gives MCP access to your calendars, contacts, even messages —
+[no small feat](https://github.com/loopwork-ai/iMCP?tab=readme-ov-file#imessage-database-access)!
+
+[Download it today](https://iMCP.app/download)
+and get a taste of some _real_ Apple intelligence.
+
+### mcp-swift-sdk
+
+In the process of building iMCP,
+we built a [Swift SDK](https://github.com/loopwork-ai/mcp-swift-sdk)
+for Model Context Protocol servers and clients.
+
+If you're inspired to build your own MCP app
+and like working in Swift more than Python or TypeScript,
+definitely give this a try!
+
+### hype
+
+If, however, you have accepted Python into your heart as I have,
+then I'd recommend checking out another project I've been working on:
+[hype](https://github.com/loopwork-ai/hype).
+
+My goal with hype is to eliminate every barrier between writing Python code
+and calling it in a way that's useful.
+Add the `@hype.up` decorator to a function
+to instantly generate an HTTP API, a CLI, a GUI, or an MCP.
+
+```python
+# example.py
+import hype
+from pydantic import Field
+
+@hype.up
+def divide(
+ x: int,
+ y: int = Field(gt=0),
+) -> int:
+ """
+ Divides one number by another.
+ :param x: The numerator
+ :param y: The denominator
+ :return: The quotient
+ """
+ return x // y
+```
+
+Start up an MCP server with the `hype` command:
+
+```console
+$ hype mcp example.py
+```
+
+### emcee
+
+But really, the best code is code you don't have to write.
+If you already have a web application with an
+[OpenAPI specification](https://www.openapis.org),
+you can use another tool we built —
+[emcee](https://emcee.sh) —
+to instantly spin up an MCP server to it.
+
+
+
+We think emcee is a convenient way to connect to services
+that don't have an existing MCP server implementation —
+_especially for services you're building yourself_.
+Got a web app with an OpenAPI spec?
+You might be surprised how far you can get without a dashboard or client library.
+
+---
+
+In case it's not abundantly clear,
+we here at NSHipster dot com are pretty bought into the promise of
+Model Context Protocol.
+And we're excited to see where everything goes in the coming months.
+
+If you're building in this space,
+I'd love to [hear from you](mailto:mattt@nshipster.com)
+✌️
+
+{% asset articles/model-context-protocol.css @inline %}
diff --git a/2025-07-25-uncertainty.md b/2025-07-25-uncertainty.md
new file mode 100644
index 00000000..c2be6d25
--- /dev/null
+++ b/2025-07-25-uncertainty.md
@@ -0,0 +1,363 @@
+---
+title: Uncertain⟨T⟩
+author: Mattt
+category: Miscellaneous
+excerpt: >-
+ GPS coordinates aren't exact.
+ Sensor readings have noise.
+ User behavior is probabilistic.
+ Yet we write code that pretends uncertainty doesn't exist,
+ forcing messy real-world data through clean Boolean logic.
+status:
+ swift: 6.0
+---
+
+You know what's wrong with people?
+They're too sure of themselves.
+
+Better to be wrong and own it than be right with caveats.
+Hard to build a personal brand out of nuance these days.
+People are attracted to confidence — however misplaced.
+
+But can you blame them? (People, that is)
+Working in software,
+the most annoying part of reaching Senior level
+is having to say _"it depends"_ all the time.
+Much more fun getting to say
+_"let's ship it and iterate"_ as Staff or
+_"that won't scale"_ as a Principal.
+
+Yet, for all of our intellectual humility,
+why do we ~~write~~ vibe code like this?
+
+```swift
+if currentLocation.distance(to: target) < 100 {
+ print("You've arrived!") // But have you, really? 🤨
+}
+```
+
+GPS coordinates aren't exact.
+They're noisy. They're approximate. They're probabilistic.
+That `horizontalAccuracy` property tucked away in your `CLLocation` object
+is trying to tell you something important:
+you're _probably_ within that radius.
+_Probably._
+
+A `Bool`, meanwhile, can be only `true` or `false`.
+That `if` statement needs to make a choice one way or another,
+but code like this doesn't capture the uncertainty of the situation.
+If truth is light,
+then current programming models collapse the wavefunction too early.
+
+## Picking the Right Abstraction
+
+In 2014, researchers at the University of Washington and Microsoft Research
+proposed a radical idea:
+What if uncertainty were encoded directly into the type system?
+Their paper,
+_[Uncertain<T>: A First-Order Type for Uncertain Data](https://www.microsoft.com/en-us/research/publication/uncertaint-a-first-order-type-for-uncertain-data-2/)_
+introduced a probabilistic programming approach that's both
+mathematically rigorous and surprisingly practical.
+
+
+
+As you'd expect for something from Microsoft in the 2010s,
+the paper is implemented in C#.
+But the concepts translate beautifully to Swift.
+
+You can find [my port on GitHub](https://github.com/mattt/Uncertain):
+
+```swift
+import Uncertain
+import CoreLocation
+
+let uncertainLocation = Uncertain.from(currentLocation)
+let nearbyEvidence = uncertainLocation.distance(to: target) < 100
+if nearbyEvidence.probability(exceeds: 0.95) {
+ print("You've arrived!") // With 2σ confidence 🤓
+}
+```
+
+When you compare two `Uncertain` values,
+you don't get a definitive `true` or `false`.
+You get an `Uncertain` that represents the _probability_ of the comparison being `true`.
+
+{% info %}
+
+Under the hood, `Uncertain` models GPS uncertainty using a
+[Rayleigh distribution](https://en.wikipedia.org/wiki/Rayleigh_distribution).
+GPS errors are typically circular around the true position,
+with error magnitude following this distribution.
+
+{% endinfo %}
+
+The same is true for other operators, too:
+
+```swift
+// How fast did we run around the track?
+let distance: Double = 400 // meters
+let time: Uncertain = .normal(mean: 60, standardDeviation: 5.0) // seconds
+let runningSpeed = distance / time // Uncertain
+
+// How much air resistance?
+let airDensity: Uncertain = .normal(mean: 1.225, standardDeviation: 0.1) // kg/m³
+let dragCoefficient: Uncertain = .kumaraswamy(alpha: 9, beta: 3) // slightly right-skewed distribution
+let frontalArea: Uncertain = .normal(mean: 0.45, standardDeviation: 0.05) // m²
+let airResistance = 0.5 * airDensity * frontalArea * dragCoefficient * (runningSpeed * runningSpeed)
+```
+
+This code builds a computation graph,
+sampling only when you ask for concrete results.
+The library uses
+[Sequential Probability Ratio Testing (SPRT)](https://en.wikipedia.org/wiki/Sequential_probability_ratio_test)
+to efficiently determine how many samples are needed —
+maybe a few dozen times for simple comparisons,
+scaling up automatically for complex calculations.
+
+```swift
+// Sampling happens only when we need to evaluate
+if ~(runningSpeed > 6.0) {
+ print("Great pace for a 400m sprint!")
+}
+// SPRT might only need a dozen samples for this simple comparison
+
+let sustainableFor5K = (runningSpeed < 6.0) && (airResistance < 50.0)
+print("Can sustain for 5K: \(sustainableFor5K.probability(exceeds: 0.9))")
+// Might use 100+ samples for this compound condition
+```
+
+
+Using an abstraction like `Uncertain` forces you to deal with uncertainty as a first-class concept
+rather than pretending it doesn't exist.
+And in doing so, you end up with much smarter code.
+
+To quote [Alan Kay](https://en.wikipedia.org/wiki/Alan_Kay):
+> Point of view is worth 80 IQ points
+> Alan Kay
+
+* * *
+
+Before we dive deeper into probability distributions,
+let's take a detour to Monaco and talk about
+[Monte Carlo sampling](https://en.wikipedia.org/wiki/Monte_Carlo_method).
+
+## The Monte Carlo Method
+
+Behold, a classic slot machine (or "fruit machine" for our UK readers 🇬🇧):
+
+```swift
+enum SlotMachine {
+ static func spin() -> Int {
+ let symbols = [
+ "◻️", "◻️", "◻️", // blanks
+ "🍒", "🍋", "🍊", "🍇", "💎"
+ ]
+
+ // Spin three reels independently
+ let reel1 = symbols.randomElement()!
+ let reel2 = symbols.randomElement()!
+ let reel3 = symbols.randomElement()!
+
+ switch (reel1, reel2, reel3) {
+ case ("💎", "💎", "💎"): return 100 // Jackpot!
+ case ("🍒", "🍒", "🍒"): return 10
+ case ("🍇", "🍇", "🍇"): return 5
+ case ("🍊", "🍊", "🍊"): return 3
+ case ("🍋", "🍋", "🍋"): return 2
+ case ("🍒", _, _), // Any cherry
+ (_, "🍒", _),
+ (_, _, "🍒"):
+ return 1
+ default:
+ return 0 // Better luck next time
+ }
+ }
+}
+```
+
+Should we play it?
+
+
+Now, we _could_ work out these probabilities analytically —
+counting combinations,
+calculating conditional probabilities,
+maybe even busting out some combinatorics.
+
+Or we could just let the computer pull the lever a bunch and see what happens.
+
+
+```swift
+let expectedPayout = Uncertain {
+ SlotMachine.spin()
+}.expectedValue(sampleCount: 10_000)
+print("Expected value per spin: $\(expectedPayout)")
+// Expected value per spin: ≈ $0.56
+```
+
+At least we know one thing for certain:
+_The house always wins._
+
+## Beyond Simple Distributions
+
+While one-armed bandits demonstrate pure randomness,
+real-world applications often deal with more predictable uncertainty.
+
+`Uncertain` provides a
+[rich set of probability distributions](https://github.com/mattt/Uncertain?tab=readme-ov-file#distribution-constructors):
+
+```swift
+// Modeling sensor noise
+let rawGyroData = 0.85 // rad/s
+let gyroReading = Uncertain.normal(
+ mean: rawGyroData,
+ standardDeviation: 0.05 // Typical gyroscope noise in rad/s
+)
+
+// User behavior modeling
+let userWillTapButton = Uncertain.bernoulli(probability: 0.3)
+
+// Network latency with long tail
+let apiResponseTime = Uncertain.exponential(rate: 0.1)
+
+// Coffee shop visit times (bimodal: morning rush + afternoon break)
+let morningRush = Uncertain.normal(mean: 8.5, standardDeviation: 0.5) // 8:30 AM
+let afternoonBreak = Uncertain.normal(mean: 15.0, standardDeviation: 0.8) // 3:00 PM
+let visitTime = Uncertain.mixture(
+ of: [morningRush, afternoonBreak],
+ weights: [0.6, 0.4] // Slightly prefer morning coffee
+)
+```
+
+{% info %}
+
+I wanted to develop an intuitive sense of how these probability distributions work,
+so I built [this companion project](https://github.com/mattt/Uncertain-Distribution-Visualizer/)
+with interactive visualizations for each one.
+It also serves as a nifty showcase for
+[Swift Charts](https://developer.apple.com/documentation/charts).
+So definitely check that out if you're uninitiated.
+
+{::nomarkdown }
+
+
+
+
+{:/}
+
+{% endinfo %}
+
+`Uncertain` also provides comprehensive
+[statistical operations](https://github.com/mattt/Uncertain?tab=readme-ov-file#statistical-operations):
+
+```swift
+// Basic statistics
+let temperature = Uncertain.normal(mean: 23.0, standardDeviation: 1.0)
+let avgTemp = temperature.expectedValue() // about 23°C
+let tempSpread = temperature.standardDeviation() // about 1°C
+
+// Confidence intervals
+let (lower, upper) = temperature.confidenceInterval(0.95)
+print("95% of temperatures between \(lower)°C and \(upper)°C")
+
+// Distribution shape analysis
+let networkDelay = Uncertain.exponential(rate: 0.1)
+let skew = networkDelay.skewness() // right skew
+let kurt = networkDelay.kurtosis() // heavy tail
+
+// Working with discrete distributions
+let diceRoll = Uncertain.categorical([1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1])!
+diceRoll.entropy() // Randomness measure (~2.57)
+(diceRoll + diceRoll).mode() // Most frequent outcome (7, perhaps?)
+
+// Cumulative probability
+if temperature.cdf(at: 25.0) < 0.2 { // P(temp ≤ 25°C) < 20%
+ print("Unlikely to be 25°C or cooler")
+}
+```
+
+The statistics are computed through sampling.
+The number of samples is configurable, letting you trade computation time for accuracy.
+
+## Putting Theory to Practice
+
+Users don't notice when things work correctly,
+but they definitely notice impossible behavior.
+When your running app claims they just sprinted at 45 mph,
+or your IRL meetup app shows someone 500 feet away when GPS accuracy is ±1000 meters,
+that's a bad look 🤡
+
+So where do we go from here?
+Let's channel our Senior+ memes from before for guidance.
+
+That Staff engineer saying _"let's ship it and iterate"_
+is right about the incremental approach.
+You can migrate uncertain calculations piecemeal
+rather than rewriting everything at once:
+
+```swift
+extension CLLocation {
+ var uncertain: Uncertain {
+ Uncertain.from(self)
+ }
+}
+
+// Gradually migrate critical paths
+let isNearby = (
+ currentLocation.uncertain.distance(to: destination) < threshold
+).probability(exceeds: 0.68)
+```
+
+And we should consider the Principal engineer's warning of _"that won't scale"_.
+Sampling has a cost, and you should understand the
+computational overhead for probabilistic accuracy:
+
+```swift
+// Fast approximation for UI updates
+let quickEstimate = speed.probability(
+ exceeds: walkingSpeed,
+ maxSamples: 100
+)
+
+// High precision for critical decisions
+let preciseResult = speed.probability(
+ exceeds: walkingSpeed,
+ confidenceLevel: 0.99,
+ maxSamples: 10_000
+)
+```
+
+{% warning %}
+
+OTOH,
+modern devices are pretty amazing.
+
+Remember kids, `Instruments.app` is your friend.
+Use profiling to guide your optimizations.
+
+Senior 🤝 Staff 🤝 Principal
+
+{% endwarning %}
+
+Start small.
+Pick one feature where GPS glitches cause user complaints.
+Replace your distance calculations with uncertain versions.
+Measure the impact.
+
+Remember:
+the goal isn't to eliminate uncertainty —
+it's to acknowledge that it exists and handle it gracefully.
+Because in the real world,
+nothing is certain except uncertainty itself.
+
+And perhaps,
+with better tools,
+we can finally stop pretending otherwise.
diff --git a/2025-08-04-isolated-any.md b/2025-08-04-isolated-any.md
new file mode 100644
index 00000000..468fdff1
--- /dev/null
+++ b/2025-08-04-isolated-any.md
@@ -0,0 +1,367 @@
+---
+title: "@isolated(any)"
+author: Matt Massicotte
+category: Swift
+excerpt: >-
+ There are cases where just a little more visibility and control over how to
+ schedule asynchronous work can make all the difference.
+status:
+ swift: 6.0
+---
+
+Ahh, `@isolated(any)`.
+It's an attribute of contradictions.
+You might see it a lot, but it's ok to ignore it.
+You don't need to use it, but I think it should be used more.
+It must always take an argument, but that argument cannot vary.
+
+Confusing? Definitely.
+But we'll get to it all.
+
+---
+
+To understand why `@isolated(any)` was introduced,
+we need to take a look at async functions.
+
+```swift
+let respondToEmergency: () async -> Void
+```
+
+This is about as simple a function type as we can get.
+But, things start to get a little more interesting
+when we look at how a function like this is used.
+A variable with this type must always be invoked with `await`.
+
+```swift
+await respondToEmergency()
+```
+
+This, of course, makes sense.
+All async functions must be called with `await`.
+But! Consider this:
+
+```swift
+let sendAmbulance: @MainActor () -> Void = {
+ print("🚑 WEE-OOO WEE-OOO!")
+}
+
+let respondToEmergency: () async -> Void = sendAmbulance
+
+await respondToEmergency()
+```
+
+The explicit types are there to help make what's going on clear.
+We first define a **synchronous** function that _**must**_ run on the `MainActor`.
+And then we assign that to a plain old,
+non-`MainActor` async function.
+We've changed so much that you might find it surprising this even compiles.
+
+Remember what `await` actually does. It allows the current task to suspend. That doesn't just let the task wait for future work to complete. It also is an opportunity to change isolation. This makes async functions very flexible!
+
+Just like a dispatcher doesn't sit there doing nothing while waiting for the ambulance to arrive, a suspended task doesn't block its thread. When the dispatcher puts you on hold to coordinate with the ambulance team, that's the isolation switch - they're transferring your request to a different department that specializes in that type of work.
+
+## But change to where, exactly?
+
+Ok, so we know that async functions, because they must always be `await`ed, gain a lot of flexibility. We are close, but have to go just a little further to find the motivation for this attribute.
+
+```swift
+func dispatchResponder(_ responder: () async -> Void) async {
+ await responder()
+}
+
+await dispatchResponder {
+ // no explicit isolation => nonisolated
+ print("🚒 HONK HOOOOONK!")
+ await airSupport()
+ print("🚁 SOI SOI SOI SOI SOI!")
+}
+
+await dispatchResponder { @MainActor in
+ print("🚑 WEE-OOO WEE-OOO!")
+}
+```
+
+We now have a function that accepts **other** functions as arguments. It's possible to pass in lots of different kinds of functions to `dispatchResponder`. They could be async functions themselves, or even be synchronous. And they can be isolated to any actor. All thanks to the power of `await`.
+
+Except there's a little problem now.
+Have a look at `dispatchResponder` on its own:
+
+```swift
+func dispatchResponder(_ responder: () async -> Void) async {
+ await responder()
+}
+```
+
+The type of `responder` fully describes everything about this function,
+**except** for one thing.
+We have no way to know its isolation.
+That information is only available at callsites.
+The isolation is still present,
+so the right thing happens at runtime.
+It's just not possible to inspect it statically or even programmatically.
+If you've encountered type erasure before,
+this should seem familiar.
+The flexibility of `async` has come with a price -
+a loss of information.
+
+This is where `@isolated(any)` comes in.
+
+## Using `@isolated(any)`
+
+We can change the definition of `dispatchResponder` to fix this.
+
+```swift
+func dispatchResponder(_ responder: @isolated(any) () async -> Void) async {
+ print("responder isolation:", responder.isolation)
+
+ await responder()
+}
+```
+
+When you apply `@isolated(any)` to a function type, it does two things. Most importantly, it gives you access to a special `isolation` property. You can use this property to inspect the isolation of the function. The isolation could be an actor. Or it could be non-isolated. This is expressible in the type system with `(any Actor)?`.
+
+Functions with properties felt really strange to me at first.
+But, after thinking for a minute,
+it became quite natural.
+Why not?
+It's just a type like any other.
+In fact, we can simulate how this all works with another feature:
+[`callAsFunction`](/callable).
+
+```swift
+struct IsolatedAnyFunction {
+ let isolation: (any Actor)?
+ let body: () async -> T
+
+ func callAsFunction() async -> T {
+ await body()
+ }
+}
+
+let value = IsolatedAnyFunction(isolation: MainActor.shared, body: {
+ // isolated work goes here
+})
+
+await value()
+```
+
+This analogy is certainly not **perfect**,
+but it's close enough that it might help.
+
+There is one other subtle change that `@isolated(any)` makes to a function
+that you should be aware of.
+Its whole purpose is to capture the isolation of a function.
+Since that could be anything,
+callsites need an opportunity to switch.
+And that means an `@isolated(any)` function must be called with an `await` —
+even if it isn't itself explicitly async.
+
+```swift
+func dispatchResponder(_ responder: @isolated(any) () -> Void) async {
+ await responder() // note the function is synchronous
+}
+```
+
+This makes synchronous functions marked with `@isolated(any)` a little strange.
+They still must be called with `await`,
+yet they aren't allowed to suspend internally?
+
+As it turns out, there are some valid (if rare) situations
+where such an arrangement can make sense.
+But adding this kind of constraint to your API
+should at least merit some extra documentation.
+
+## How @isolated(any) Affects Callers
+
+All of the task creation APIs —
+`Task` initializers and `TaskGroup` —
+make use of `@isolated(any)`.
+These are used a lot
+and are usually encountered very early on when learning about concurrency.
+So, it's completely natural to run into this attribute and think:
+
+_"Ugh another thing to understand!"_
+
+It's reasonable because
+the components of a function type dictate how it can be used.
+They are all essential qualities for API consumers.
+They _**are**_ the interface.
+
+- Parameters
+- Return value
+- Does it throw?
+- Is it async?
+
+This is not an exhaustive list,
+but what's important is all of these are things callers must care about.
+Except for `@isolated(any)`, which is the **opposite**.
+It doesn't affect callers at all.
+
+This, I think, is the root of a lot of confusion around `@isolated(any)`.
+Unlike other qualities of a function,
+this attribute is used to capture information for the API producer.
+
+I'm so close to saying _"you can and should just ignore `@isolated(any)`"_.
+But I just cannot quite go that far,
+because there is one situation you should be aware of.
+
+## Scheduling
+
+To help understand when you should be thinking about using `@isolated(any)`,
+I'm going to quote
+[the proposal](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0431-isolated-any-functions.md):
+
+> This allows the API to make more **intelligent scheduling** decisions about the function.
+
+I've highlighted "intelligent scheduling",
+because this is the key component of `@isolated(any)`.
+The attribute gives you access to the isolation of a function argument.
+But what would you use that for?
+
+Did you know that, before Swift 6.0, the ordering of the following code was undefined?
+
+```swift
+@MainActor
+func threeAlarmFire() {
+ Task { print("🚒 Truck A reporting!") }
+ Task { print("🚒 Truck B checking in!") }
+ Task { print("🚒 Truck C on the case!") }
+}
+```
+
+Ordering turns out to be a very tricky topic when working with unstructured tasks.
+And while it will always require care, Swift 6.0 did improve the situation.
+We now have some stronger guarantees about scheduling work on the `MainActor`,
+and `@isolated(any)` was needed to make that possible.
+
+{% warning %}
+
+Anytime you use `Task`,
+think about _when_ that work will start and _how_ that could matter.
+
+{% endwarning %}
+
+Take a look at this:
+
+```swift
+@MainActor
+func sendAmbulance() {
+ print("🚑 WEE-OOO WEE-OOO!")
+}
+
+nonisolated func dispatchResponders() {
+ // synchronously enqueued
+ Task { @MainActor in
+ sendAmbulance()
+ }
+
+ // synchronously enqueued
+ Task(operation: sendAmbulance)
+
+ // not synchronously enqueued!
+ Task {
+ await sendAmbulance()
+ }
+}
+```
+
+These are three ways to achieve the same goal.
+But, there is a subtle difference in how the last form is scheduled.
+`Task` takes an `@isolated(any)` function
+so it can look at its isolation
+and **synchronously submit it to an actor**.
+This is how ordering can be preserved!
+But, it cannot do that in the last case.
+That closure passed into `Task` isn't actually itself `MainActor` —
+it has inherited nonisolated from the enclosing function.
+
+I think it might help to translate this into
+GCD.
+
+```swift
+func dispatchResponders() {
+ // synchronously enqueued
+ DispatchQueue.main.async {
+ sendAmbulance()
+ }
+
+ // synchronously enqueued
+ DispatchQueue.main.async(execute: sendAmbulance)
+
+ // not synchronously enqueued!
+ DispatchQueue.global().async {
+ DispatchQueue.main.async {
+ sendAmbulance()
+ }
+ }
+}
+```
+
+Look really closely at that last one!
+What we are doing there is introducing a new async closure
+that then calls our `MainActor` function.
+There are **two** steps.
+This doesn't always matter,
+but it certainly could.
+And if you need to precisely schedule asynchronous work,
+`@isolated(any)` can help.
+
+## isolated(all)
+
+All this talk about `@isolated(any)` got me thinking...
+
+It's kinda strange that only _some_ functions get to have this `isolation` property.
+It would certainly feel more consistent to me if _all_ functions had it.
+In fact, I think we can go further.
+I can imagine a future where an explicit `@isolated(any)`
+isn't even necessary for async functions.
+As far as I can tell, there is no downside.
+
+And a little less syntactic noise would be nice.
+Perhaps one day!
+
+## isolated(some)
+
+We do have to talk about that `any`.
+It's surprising that this attribute requires an argument,
+yet permits only one possible value.
+The reason here comes down to future considerations.
+
+The **concrete** actor type that this `isolation` property returns
+is always `(any Actor)?`.
+This is the most generic type for isolation and matches the `#isolation` macro.
+Today, there is no way to constrain a function to only **specific** actor types,
+such as `@isolated(MyActor)`.
+The `any` keyword here was chosen to mirror how protocols handle this.
+But accepting an argument leaves the door open
+to more sophisticated features in the future.
+
+And that really fits the spirit of `@isolated(any)`.
+Doing a little work now in exchange for flexibility down the road.
+
+Because you'll see it in many foundational concurrency APIs,
+it's very natural to feel like you must understand `@isolated(any)`.
+I'm 100% behind technical curiosity!
+In this case, however, it is not required.
+For the most part, you can just ignore this attribute.
+You will rarely, if ever, need to use it yourself.
+
+But if you ever find yourself capturing isolated functions
+and passing them along to **other** APIs that use `@isolated(any)`,
+you should consider adopting it.
+It could prove useful.
+It's even a source-compatible change
+to add or remove this attribute from an async function.
+
+---
+
+So there you have it.
+
+As with many parts of the concurrency system,
+there's a surprising depth to `@isolated(any)`.
+Thankfully, from a practical perspective,
+we can enjoy the ordering guarantees of task creation
+that it enables without needing to master it.
+And one less thing on this journey is most welcome.
+
+Isolated maybe, but never alone.
diff --git a/2025-10-01-manim.md b/2025-10-01-manim.md
new file mode 100644
index 00000000..4cb90e9a
--- /dev/null
+++ b/2025-10-01-manim.md
@@ -0,0 +1,400 @@
+---
+title: Manim
+author: Mattt
+category: Miscellaneous
+excerpt: >-
+ This year for Halloween,
+ NSHipster is dressing up as 3blue1brown.
+ And thanks to his open-source math animation library,
+ we're _totally_ pulling it off.
+status:
+ swift: 6.2
+---
+
+A few years ago,
+Twitter user [@38mo1](https://x.com/38mo1/),
+a chemistry teacher from Japan,
+posted a [Halloween × Boolean logic meme](https://x.com/38mo1/status/1320004943542009857)
+that I liked enough to save to `~/Downloads`.
+And ever since,
+I've had a yearly reminder every October 1st
+to open the image and get a seasonal chuckle 🎃
+
+More recently,
+I wanted to teach my kids some advanced snake maths
+([long story](https://www.youtube.com/watch?v=ysaIAwxl7fc) 🐍)
+and remembered that the software
+[3blue1brown](https://www.youtube.com/c/3blue1brown) uses for their videos was open-source.
+
+Which is all to say that this year for Halloween,
+NSHipster is dressing up as a beloved YouTuber.
+How'd I do?
+
+
+
+_Neat, right?_
+Everything used to create that video
+[available on GitHub](https://github.com/nshipster/trick-xor-treat).
+Read on to learn more about how to get started on your next great explainer.
+
+---
+
+Back in 2015, Grant Sanderson found himself at a hackathon
+with a desire to practice his Python skills.
+What emerged was "very scrappy code for visualizing functions as transformations" —
+a playful experiment that would eventually become the foundation of both
+the 3Blue1Brown YouTube channel
+(now 6.5+ million subscribers strong)
+and Manim itself.
+
+{% info %}
+By 2020, the community wanted stability and documentation.
+A fork emerged: **Manim Community Edition (ManimCE)** for general use,
+while Sanderson's version became **ManimGL** for his own experimentation.
+Today both versions thrive with distinct purposes.
+{% endinfo %}
+
+## 0 to 1
+
+Manim has a reputation for being difficult to install.
+Some of this is a natural consequence for a tool
+whose audience extends to self-described non-programmers.
+Some of this is intrinsic to working with motion graphics.
+And being written in Python doesn't do it any favors
+(though `uv` pretty much solves all the complaints anyone could have).
+
+Docker was more or less invented to solve the problem of
+packaging Python applications with system dependencies,
+so it's entirely appropriate to wash your hands of everything and
+[run Manim with Docker](https://docs.manim.community/en/stable/installation/docker.html):
+
+```shell
+$ docker run --rm -it -v "/full/path/to/your/directory:/manim" manimcommunity/manim manim -qm scene.py MySceneName
+```
+
+...but that feels like giving up.
+
+And besides,
+you'll appreciate having a proper Python development environment
+once you're up-and-running.
+Stick with me,
+and I promise everything will be A-OK (pinky swear 🤙).
+
+### An opinionated setup guide for Manim on macOS in {{ site.time | date: '%Y' }}
+
+First,
+let's do some mise en place:
+
+```shell
+# Install mise
+$ brew install mise
+
+# Install Python and uv
+$ mise use -g python@3.13 uv@latest
+```
+
+- [Homebrew](https://brew.sh) is a package manager for macOS.
+ It's the best way to install the system dependencies we need to run Manim.
+- [Mise](https://mise.jdx.dev) is a polyglot tool manager.
+ While you _could_ use Homebrew to install Python,
+ you often need to keep a few versions around to work across different projects.
+ So that's what we'll recommend here.
+
+- [uv](https://docs.astral.sh/uv/) is a Python package manager.
+ It's wicked fast and _just works™_.
+
+
+Now,
+let's create our project and make ourselves at home:
+
+```shell
+# Create a new project
+$ uv init my-explainer
+
+$ cd my-explainer
+# Now open with your preferred $EDITOR
+```
+
+Next,
+go ahead and install Manim's system dependencies:
+
+```shell
+# Install Manim dependencies
+$ brew install pkg-config cairo # for graphics
+$ brew install --cask mactex-no-gui # for ƒοrmυℓαѕ
+$ brew install sox # for voiceovers
+```
+
+- [Cairo](https://www.cairographics.org) is a 2D graphics library.
+ You need `pkg-config` to get the Pycairo bindings working properly.
+- [MacTex](https://www.tug.org/mactex/) is a LaTeX distribution for macOS.
+ You'll need this to make a proper maths explainer with impressive-looking formulas.
+ We recommend downloading `mactex-no-gui`,
+ which foregoes the GUI apps that ship in the full version.
+- [SoX](https://sourceforge.net/projects/sox/) is a utility for
+ converting audio files to different formats.
+ Manim has some nice-_ish_ affordances for putting voice-overs into your scenes,
+ and you'll need SoX to take advantage of them.
+
+Finally,
+let's run a health check to see if we're still on the happy path:
+
+```shell
+# Is everything working? (🫣)
+$ uv run manim checkhealth
+```
+
+_Huzzah!_
+
+
+
+## Setting the Scene
+
+The Manim API is [rich and well-documented](https://docs.manim.community/en/stable/tutorials_guides.html),
+with an [extensive collection of examples](https://docs.manim.community/en/stable/examples.html).
+Rather than attempt to explain everything here,
+let's look at one concrete example to get a sense of how it all works.
+
+Here's a standalone "Trick XOR Treat" scene:
+
+```python
+from manim import *
+
+
+class TrickXORTreat(Scene):
+ def construct(self):
+ # Panel background
+ panel_bg = RoundedRectangle(width=7, height=5, corner_radius=0.2).set_fill(
+ GREY, opacity=1
+ )
+
+ # Title
+ title = Text("Trick XOR Treat").scale(0.8)
+ title.move_to(panel_bg.get_top() + DOWN * 0.4)
+
+ # Two circles for the Venn diagram
+ c1 = Circle(radius=1.4, color=BLACK).move_to(LEFT * 0.8)
+ c2 = Circle(radius=1.4, color=BLACK).move_to(RIGHT * 0.8)
+
+ # Create individual fill shapes for XOR (exclusive OR)
+ left_only = Difference(c1, c2).set_fill(ORANGE, opacity=0)
+ right_only = Difference(c2, c1).set_fill(ORANGE, opacity=0)
+ center = Intersection(c1, c2).set_fill(GREY, opacity=0) # XOR excludes center
+
+ # Create faces on left and right sections
+ face = VGroup()
+ # Left face
+ left_eye = Circle(radius=0.2, color=BLACK, fill_opacity=1)
+ left_eye.move_to(left_only.get_center() + UL * 0.3)
+ left_mouth = ParametricFunction(
+ lambda t: [t, 0.15 * np.sin(t * PI * 5), 0], t_range=[-0.3, 0.3]
+ ).set_stroke(color=BLACK, width=8)
+ left_mouth.move_to(left_only.get_center() + DOWN * 0.2)
+ # Right face
+ right_eye = Circle(radius=0.2, color=BLACK, fill_opacity=1)
+ right_eye.move_to(right_only.get_center() + UR * 0.3)
+ right_mouth = ParametricFunction(
+ lambda t: [t, 0.15 * np.sin(t * PI * 5), 0], t_range=[-0.3, 0.3]
+ ).set_stroke(color=BLACK, width=8)
+ right_mouth.move_to(right_only.get_center() + DOWN * 0.2)
+ face.add(left_eye, left_mouth, right_eye, right_mouth)
+
+ # Combine everything
+ panel = VGroup(
+ panel_bg,
+ left_only,
+ right_only,
+ center,
+ VGroup(c1, c2), # Circle outlines
+ title,
+ face,
+ )
+ panel.move_to(ORIGIN)
+
+ # Animate the panel appearing with smooth scaling
+ self.play(FadeIn(panel_bg, scale=1.1), Write(title, run_time=1.2), run_time=1.0)
+
+ # Draw circles sequentially
+ self.play(Create(c1, run_time=1.0))
+ self.wait(0.5)
+ self.play(Create(c2, run_time=1.0))
+ self.wait(0.5)
+
+ # Dramatic filling animations with smooth rate functions
+ self.play(
+ left_only.animate.set_fill(ORANGE, opacity=1),
+ run_time=1.2,
+ rate_func=smooth,
+ )
+ self.wait(0.5)
+
+ self.play(
+ right_only.animate.set_fill(ORANGE, opacity=1),
+ run_time=1.2,
+ rate_func=smooth,
+ )
+ self.wait(0.5)
+
+ # Animate faces appearing on top of fills with bounce
+ self.play(FadeIn(face, shift=UP * 0.3, scale=0.8), run_time=0.8)
+
+ # Final pause
+ self.wait(1.5)
+
+```
+
+The Manim API is refreshingly procedural—
+a nice break from the declarative nature of SwiftUI, if you ask me.
+You create objects, position them, and then explicitly tell them what to do.
+The `.animate` syntax provides some syntactic sugar for transformations,
+but at its core, this is imperative programming at its finest.
+
+{% warning %}
+
+As far as I can tell, `Scene` objects aren't composable.
+Or at least there wasn't an immediately obvious way to do so
+(please correct me if I'm wrong).
+Rather than fussing over that,
+I plowed ahead with writing a giant `construct` method.
+
+{% endwarning %}
+
+## Enter the Development Loop
+
+Our example project includes a Mise task for quickly previewing our scene.
+It runs Manim at a low
+[resolution](https://www.youtube.com/watch?v=1unkluyh2Ks) (480p) and
+[frame rate](https://www.youtube.com/watch?v=DyqjTZHRdRs) (15 fps),
+and then opens the generated file in QuickTime Player.
+
+
+
+
+
+```shell
+$ mise run preview
+```
+
+The project also has a `render` task for when you're ready to share it around:
+
+```shell
+$ mise run render
+```
+
+If everything works as intended, you should see something like this:
+
+
+
+## (Voiceover)
+
+No 3blue1brown explainer video would be complete
+without the dulcet baritone of its creator.
+Alas,
+Mr. Sanderson had previous obligations that took him away from our costume party,
+so we'll have to improvise.
+Let's crank up the spook factor and have an AI... _ghost? I guess?_ narrate our flick.
+
+[ElevenLabs](https://elevenlabs.io) has some scary-good text-to-speech models.
+While `manim-voiceover` provides integration,
+it's currently a bit temperamental with Python 3.13
+and doesn't support the latest
+[v3 models](https://elevenlabs.io/v3)
+for hyper-realistic voiceovers.
+
+{% error %}
+
+The official `manim-voiceover` package doesn't play nicely with Python 3.13,
+and doesn't support ElevenLabs' SOTA v3 models.
+I found [a fork](https://pypi.org/project/manim-voiceover-fixed/)
+that got me what I needed, but YMMV.
+
+Here's the error message I wrestled with for the better part of an hour, for posterity:
+
+```console
+uv add manim-voiceover[elevenlabs,transcribe] openai-whisper
+ × Failed to build `openai-whisper==20230314`
+ ├─▶ The build backend returned an error
+ ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel` failed (exit status: 1)
+```
+
+{% enderror %}
+
+To get started, you import from `manim_voiceover` (`_fixed`)
+and update the scene to inherit from `VoiceoverScene`.
+
+```python
+from manim_voiceover_fixed import VoiceoverScene
+from manim_voiceover_fixed.services.elevenlabs import ElevenLabsService
+
+class TrickXORTreat(VoiceoverScene):
+ def construct(self):
+ self.set_speech_service(
+ ElevenLabsService(
+ voice_name="Liam",
+ model="eleven_v3",
+ transcription_model=None, # <-- workaround for https://github.com/ManimCommunity/manim-voiceover/issues/114
+ )
+ )
+
+ with self.voiceover(
+ text="""[annoyingly high-pitch nasal pedantic] Well, actually,
+ it's an exclusive or, also known as XOR..."""
+ ):
+ # This wait() automatically waits until the voiceover finishes!
+ self.wait()
+```
+
+The `wait()` inside the context manager waits until the voiceover finishes —
+no manual timing required.
+For recorded voiceovers especially,
+this dramatically cuts down on how much time you spend in post
+getting the timing right.
+In fact, the finished video at the top was entirely rendered by Manim—
+without any touch-ups in Final Cut or Premiere!
+
+
+
+The first time you run this code,
+Manim will helpfully (albeit unexpectedly) prompt you to enter an API token,
+which you can get [in your account dashboard](https://elevenlabs.io/app/developers).
+That'll get saved to an [`.env` file](https://nshipster.com/1password-cli/)
+and that should be the end of it.
+
+---
+
+The best explainer videos don't just teach,
+they spark curiosity, they delight, they make complex ideas feel approachable.
+That's what drew me to 3blue1brown in the first place,
+and why I've kept that silly meme in my Downloads folder all these years.
+
+So if you've been sitting on a meme in your `~/Downloads` folder,
+or just want an excuse to dust off those Python skills,
+maybe this Halloween _you_ dress up as a math YouTuber, too.
+The costume's open source, and honestly?
+It's a pretty good look.
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 00000000..2d957ecc
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,160 @@
+# Attribution-NonCommercial 4.0 International
+
+Creative Commons Corporation (“Creative Commons”) is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an “as-is” basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible.
+
+### Using Creative Commons Public Licenses
+
+Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses.
+
+- **Considerations for licensors:** Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC-licensed material, or material used under an exception or limitation to copyright. [More considerations for licensors](http://wiki.creativecommons.org/Considerations_for_licensors_and_licensees#Considerations_for_licensors).
+
+- **Considerations for the public:** By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor’s permission is not necessary for any reason–for example, because of any applicable exception or limitation to copyright–then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. [More considerations for the public](http://wiki.creativecommons.org/Considerations_for_licensors_and_licensees#Considerations_for_licensees).
+
+## Creative Commons Attribution-NonCommercial 4.0 International Public License
+
+By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-NonCommercial 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.
+
+### Section 1 – Definitions.
+
+a. **Adapted Material** means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image.
+
+b. **Adapter's License** means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License.
+
+c. **Copyright and Similar Rights** means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights.
+
+d. **Effective Technological Measures** means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements.
+
+e. **Exceptions and Limitations** means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material.
+
+f. **Licensed Material** means the artistic or literary work, database, or other material to which the Licensor applied this Public License.
+
+g. **Licensed Rights** means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license.
+
+h. **Licensor** means the individual(s) or entity(ies) granting rights under this Public License.
+
+i. **NonCommercial** means not primarily intended for or directed towards commercial advantage or monetary compensation. For purposes of this Public License, the exchange of the Licensed Material for other material subject to Copyright and Similar Rights by digital file-sharing or similar means is NonCommercial provided there is no payment of monetary compensation in connection with the exchange.
+
+j. **Share** means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them.
+
+k. **Sui Generis Database Rights** means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world.
+
+l. **You** means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning.
+
+### Section 2 – Scope.
+
+a. **_License grant._**
+
+1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to:
+
+ A. reproduce and Share the Licensed Material, in whole or in part, for NonCommercial purposes only; and
+
+ B. produce, reproduce, and Share Adapted Material for NonCommercial purposes only.
+
+2. **Exceptions and Limitations.** For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions.
+
+3. **Term.** The term of this Public License is specified in Section 6(a).
+
+4. **Media and formats; technical modifications allowed.** The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material.
+
+
+ 5. __Downstream recipients.__
+
+ A. __Offer from the Licensor – Licensed Material.__ Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License.
+
+ B. __No downstream restrictions.__ You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material.
+
+ 6. __No endorsement.__ Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i).
+
+b. **_Other rights._**
+
+1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise.
+
+2. Patent and trademark rights are not licensed under this Public License.
+
+3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties, including when the Licensed Material is used other than for NonCommercial purposes.
+
+### Section 3 – License Conditions.
+
+Your exercise of the Licensed Rights is expressly made subject to the following conditions.
+
+a. **_Attribution._**
+
+1. If You Share the Licensed Material (including in modified form), You must:
+
+ A. retain the following if it is supplied by the Licensor with the Licensed Material:
+
+ i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated);
+
+ ii. a copyright notice;
+
+ iii. a notice that refers to this Public License;
+
+ iv. a notice that refers to the disclaimer of warranties;
+
+ v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable;
+
+ B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and
+
+ C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License.
+
+2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information.
+
+3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable.
+
+4. If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License.
+
+### Section 4 – Sui Generis Database Rights.
+
+Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material:
+
+a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database for NonCommercial purposes only;
+
+b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and
+
+c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database.
+
+For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights.
+
+### Section 5 – Disclaimer of Warranties and Limitation of Liability.
+
+a. **Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.**
+
+b. **To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.**
+
+c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability.
+
+### Section 6 – Term and Termination.
+
+a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically.
+
+b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates:
+
+1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or
+
+2. upon express reinstatement by the Licensor.
+
+For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License.
+
+c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License.
+
+d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License.
+
+### Section 7 – Other Terms and Conditions.
+
+a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed.
+
+b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License.
+
+### Section 8 – Interpretation.
+
+a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License.
+
+b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions.
+
+c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor.
+
+d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority.
+
+> Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at [creativecommons.org/policies](http://creativecommons.org/policies), Creative Commons does not authorize the use of the trademark “Creative Commons” or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses.
+>
+> Creative Commons may be contacted at creativecommons.org