Skip to content

Commit

Permalink
Merging
Browse files Browse the repository at this point in the history
  • Loading branch information
mattt committed Nov 13, 2014
1 parent 47ee048 commit ebbc1c6
Show file tree
Hide file tree
Showing 22 changed files with 502 additions and 492 deletions.
74 changes: 37 additions & 37 deletions 2012-07-24-nssortdescriptor.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,6 @@ 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`.

~~~{objective-c}
@interface Person : NSObject
@property NSString *firstName;
@property NSString *lastName;
@property NSNumber *age;
@end
@implementation Person
- (NSString *)description {
return [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName];
}
@end
~~~

~~~{swift}
class Person: NSObject {
let firstName: String
Expand All @@ -60,6 +44,22 @@ class Person: NSObject {
}
~~~

~~~{objective-c}
@interface Person : NSObject
@property NSString *firstName;
@property NSString *lastName;
@property NSNumber *age;
@end
@implementation Person
- (NSString *)description {
return [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName];
}
@end
~~~

Given the following dataset:

| `firstName` | `lastName` | `age` |
Expand All @@ -71,6 +71,27 @@ Given the following dataset:

Here are some of the different ways they can be sorted by combinations of `NSSortDescriptor`:

~~~{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: "localizedStandardCompare:")
let lastNameSortDescriptor = NSSortDescriptor(key: "lastName", ascending: true, selector: "localizedStandardCompare:")
let ageSortDescriptor = NSSortDescriptor(key: "age", ascending: false)
let sortedByAge = (people as NSArray).sortedArrayUsingDescriptors([ageSortDescriptor])
// "Charlie Smith", "Quentin Alberts", "Bob Jones", "Alice Smith"
let sortedByFirstName = (people as NSArray).sortedArrayUsingDescriptors([firstNameSortDescriptor])
// "Alice Smith", "Bob Jones", "Charlie Smith", "Quentin Alberts"
let sortedByLastNameFirstName = (people as NSArray).sortedArrayUsingDescriptors([lastNameSortDescriptor, firstNameSortDescriptor])
// "Quentin Alberts", "Bob Jones", "Alice Smith", "Charlie Smith"
~~~

~~~{objective-c}
NSArray *firstNames = @[ @"Alice", @"Bob", @"Charlie", @"Quentin" ];
NSArray *lastNames = @[ @"Smith", @"Jones", @"Smith", @"Alberts" ];
Expand Down Expand Up @@ -106,27 +127,6 @@ NSLog(@"By last name, first name: %@", [people sortedArrayUsingDescriptors:@[las
// "Quentin Alberts", "Bob Jones", "Alice Smith", "Charlie Smith"
~~~

~~~{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: "localizedStandardCompare:")
let lastNameSortDescriptor = NSSortDescriptor(key: "lastName", ascending: true, selector: "localizedStandardCompare:")
let ageSortDescriptor = NSSortDescriptor(key: "age", ascending: false)
let sortedByAge = (people as NSArray).sortedArrayUsingDescriptors([ageSortDescriptor])
// "Charlie Smith", "Quentin Alberts", "Bob Jones", "Alice Smith"
let sortedByFirstName = (people as NSArray).sortedArrayUsingDescriptors([firstNameSortDescriptor])
// "Alice Smith", "Bob Jones", "Charlie Smith", "Quentin Alberts"
let sortedByLastNameFirstName = (people as NSArray).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.
Expand Down
120 changes: 63 additions & 57 deletions 2012-07-31-nsdatecomponents.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,67 +14,62 @@ Whereas dates represent a particular moment in time, date components depend on w

`NSDateComponents` can be initialized and manipulated manually, but most often, they're extracted from a specified date, using `NSCalendar -components:fromDate:`:

~~~{objective-c}
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *date = [NSDate date];
[calendar components:(NSDayCalendarUnit | NSMonthCalendarUnit) fromDate:date];
~~~
~~~{swift}
let calendar = NSCalendar.currentCalendar()
let date = NSDate()
let components = calendar.components(.MonthCalendarUnit | .DayCalendarUnit, fromDate: date)
~~~

~~~{objective-c}
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *date = [NSDate date];
[calendar components:(NSDayCalendarUnit | NSMonthCalendarUnit) 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:

<!-- - `NSEraCalendarUnit`
- `NSYearCalendarUnit`
- `NSMonthCalendarUnit`
- `NSDayCalendarUnit`
- `NSHourCalendarUnit`
- `NSMinuteCalendarUnit`
- `NSSecondCalendarUnit`
- `NSWeekCalendarUnit`
- `NSWeekdayCalendarUnit`
- `NSWeekdayOrdinalCalendarUnit`
- `NSQuarterCalendarUnit`
- `NSWeekOfMonthCalendarUnit`
- `NSWeekOfYearCalendarUnit`
- `NSYearForWeekOfYearCalendarUnit`
- `NSCalendarCalendarUnit`
- `NSTimeZoneCalendarUnit` -->
- `.CalendarUnitEra`
- `.CalendarUnitYear`
- `.CalendarUnitMonth`
- `.CalendarUnitDay`
- `.CalendarUnitHour`
- `.CalendarUnitMinute`
- `.CalendarUnitSecond`
- `.CalendarUnitWeekday`
- `.CalendarUnitQuarter`
- `.CalendarUnitWeekOfMonth`
- `.CalendarUnitWeekOfYear`
- `.CalendarUnitYearForWeekOfYear`
- `.CalendarUnitCalendar`
- `.CalendarUnitTimeZone`
~~~{swift}
.CalendarUnitEra
.CalendarUnitYear
.CalendarUnitMonth
.CalendarUnitDay
.CalendarUnitHour
.CalendarUnitMinute
.CalendarUnitSecond
.CalendarUnitWeekday
.CalendarUnitQuarter
.CalendarUnitWeekOfMonth
.CalendarUnitWeekOfYear
.CalendarUnitYearForWeekOfYear
.CalendarUnitCalendar
.CalendarUnitTimeZone
~~~

~~~{objective-c}
NSEraCalendarUnit
NSYearCalendarUnit
NSMonthCalendarUnit
NSDayCalendarUnit
NSHourCalendarUnit
NSMinuteCalendarUnit
NSSecondCalendarUnit
NSWeekCalendarUnit
NSWeekdayCalendarUnit
NSWeekdayOrdinalCalendarUnit
NSQuarterCalendarUnit
NSWeekOfMonthCalendarUnit
NSWeekOfYearCalendarUnit
NSYearForWeekOfYearCalendarUnit
NSCalendarCalendarUnit
NSTimeZoneCalendarUnit
~~~

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

~~~{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]);
~~~

~~~{swift}
let calendar = NSCalendar.currentCalendar()
let date = NSDate()
Expand All @@ -86,24 +81,21 @@ components.hour = 12
println("1 week and 12 hours from now: \(calendar.dateByAddingComponents(components, toDate: date, options: nil))")
~~~

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

~~~{objective-c}
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *date = [NSDate date];
NSDateComponents *components = [[NSDateComponents alloc] init];
[components setYear:1987];
[components setMonth:3];
[components setDay:17];
[components setHour:14];
[components setMinute:20];
[components setSecond:0];
[components setWeekOfYear:1];
[components setHour:12];
NSDate *date = [calendar dateFromComponents:components];
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: NSGregorianCalendar)
Expand All @@ -118,6 +110,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.
Expand Down
43 changes: 22 additions & 21 deletions 2012-08-13-nsincrementalstore.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,6 @@ 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`:

~~~{objective-c}
+ (void)initialize {
[NSPersistentStoreCoordinator registerStoreClass:self forStoreType:[self type]];
}
+ (NSString *)type {
return NSStringFromClass(self);
}
~~~

~~~{swift}
class CustomIncrementalStore: NSIncrementalStore {
override class func initialize() {
Expand All @@ -60,17 +50,21 @@ class CustomIncrementalStore: NSIncrementalStore {
}
~~~

### `-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:

~~~{objective-c}
NSMutableDictionary *mutableMetadata = [NSMutableDictionary dictionary];
[mutableMetadata setValue:[[NSProcessInfo processInfo] globallyUniqueString] forKey:NSStoreUUIDKey];
[mutableMetadata setValue:[[self class] type] forKey:NSStoreTypeKey];
[self setMetadata:mutableMetadata];
+ (void)initialize {
[NSPersistentStoreCoordinator registerStoreClass:self forStoreType:[self type]];
}
+ (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}
override func loadMetadata(error: NSErrorPointer) -> Bool {
self.metadata = [
Expand All @@ -82,6 +76,13 @@ override func loadMetadata(error: NSErrorPointer) -> Bool {
}
~~~

~~~{objective-c}
NSMutableDictionary *mutableMetadata = [NSMutableDictionary dictionary];
[mutableMetadata setValue:[[NSProcessInfo processInfo] globallyUniqueString] forKey:NSStoreUUIDKey];
[mutableMetadata setValue:[[self class] type] forKey:NSStoreTypeKey];
[self setMetadata:mutableMetadata];
~~~

### `-executeRequest:withContext:error:`

Here's where things get interesting, from an implementation standpoint. (And where it all goes to hell, from an API design standpoint)
Expand Down Expand Up @@ -132,14 +133,14 @@ 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:

~~~{objective-c}
[self newObjectIDForEntity:entity referenceObject:[NSNumber numberWithUnsignedInteger:rowID]];
~~~

~~~{swift}
self.newObjectIDForEntity(entity, referenceObject: rowID)
~~~

~~~{objective-c}
[self newObjectIDForEntity:entity referenceObject:[NSNumber numberWithUnsignedInteger:rowID]];
~~~

## Roll Your Own Core Data Backend

Going through all of the necessary methods to override in an `NSIncrementalStore` subclass, you may have found your mind racing with ideas about how you might implement a SQL or NoSQL store, or maybe something new altogether.
Expand Down
22 changes: 11 additions & 11 deletions 2012-09-03-nslocale.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,19 +93,19 @@ So if you were building a component that added quotations around arbitrary text,

Another impressive, albeit mostly-useless method is `-displayNameForKey:value:`, which can return the display name of a locale identifier (`NSLocaleIdentifier`):

~~~{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"]);
~~~

~~~{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)"

Expand All @@ -119,11 +119,6 @@ One final method worth mentioning is `NSLocale +preferredLanguages`, which retur

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:

~~~{objective-c}
NSMutableURLRequest *request = ...;
[request setValue:[NSString stringWithFormat:@"%@", [[NSLocale preferredLanguages] componentsJoinedByString:@", "]], forHTTPHeaderField:@"Accept-Language"];
~~~

~~~{swift}
// Accept-Language HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
let acceptLanguage: String = {
Expand All @@ -145,6 +140,11 @@ 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!

---
Expand Down
Loading

0 comments on commit ebbc1c6

Please sign in to comment.