fetchRequest.sortDescriptors = [NSSortDescriptor(key: “id”, ascending: true, selector: “localizedStandardCompare:” )]
fetchRequest.sortDescriptors = [NSSortDescriptor(key: “id”, ascending: true, selector: “localizedStandardCompare:” )]
original source : https://nshipster.com/nssortdescriptor/
core data에서 sort의 기본을 알수 있다.
my review point is 9/10
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:
key
: for a given collection, the key for the corresponding value to be sorted on for each object in the collection.ascending
: a boolean specifying whether the collection should be sorted in ascending (YES
) or descending (NO
) order.
There is an optional third parameter that relates to how the sorted values are compared to one another. By default, this is a simple equality check, but this behavior can be changed by passing either a selector
(SEL
) or comparator
(NSComparator
).
Any time you’re sorting user-facing strings, be sure to pass the selector localizedStandardCompare:
, which will sort according to the language rules of the current locale (locales may differ on ordering of case, diacritics, and so forth).
Collection classes like NSArray
and NSSet
have methods to return sorted arrays of the objects that take an array of sortDescriptors
. Sort descriptors are applied in order, so that if two elements happen to be tied for a particular sorting criteria, the tie is broken by any subsequent descriptors.
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
.
class Person: NSObject {
let firstName: String
let lastName: String
let age: Int
init(firstName: String, lastName: String, age: Int) {
self.firstName = firstName
self.lastName = lastName
self.age = age
}
override var description: String {
return "(firstName) (lastName)"
}
}
Given the following dataset:

Here are some of the different ways they can be sorted by combinations of NSSortDescriptor
:
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.
Because, in reality, sorting should be thought of in terms of business logic, not mathematical formulas and map-reduce functions. In this respect, NSSortDescriptor
is a slam dunk, and will have you pining for it anytime you venture out of Objective-C and Cocoa.
nsmutablehipster
Questions? Corrections? Issues and pull requests are always welcome — NSHipster is made better by readers like you.
This article uses Swift version 1.1. Find status information for all articles on the status page.
my review point is 9/10
chatting app의 메시지 표시창 구성의 기본을 알수 있음.
https://youtu.be/hz1h_ColGy0?t=5m29s NSDate()을 이용한 시간 변경
https://youtu.be/hz1h_ColGy0?t=7m core data sorting
https://youtu.be/hz1h_ColGy0?t=8m core data predicate
https://youtu.be/hz1h_ColGy0?t=14m45s array sort (using ‘compare’ method )
https://youtu.be/hz1h_ColGy0?t=17m10s collection view controller에서 cell을 선택한 경우 다른 controller로 연결되게 하는 작업 (programticallay)
https://youtu.be/hz1h_ColGy0?t=20m10s collection view 내용, 모양 변경 (programmatically)
my review point is 9/10
chatting app의 메시지 표시창 구성의 기본을 알수 있음.
https://youtu.be/hz1h_ColGy0?t=5m29s NSDate()을 이용한 시간 변경
https://youtu.be/hz1h_ColGy0?t=7m core data sorting
https://youtu.be/hz1h_ColGy0?t=8m core data predicate
https://youtu.be/hz1h_ColGy0?t=14m45s array sort (using ‘compare’ method )
https://youtu.be/hz1h_ColGy0?t=17m10s collection view controller에서 cell을 선택한 경우 다른 controller로 연결되게 하는 작업 (programticallay)
https://youtu.be/hz1h_ColGy0?t=20m10s collection view 내용, 모양 변경 (programmatically)
my review point is 9/10
chatting app의 메시지 표시창 구성의 기본을 알수 있음.
https://youtu.be/hz1h_ColGy0?t=5m29s NSDate()을 이용한 시간 변경
https://youtu.be/hz1h_ColGy0?t=7m core data sorting
https://youtu.be/hz1h_ColGy0?t=8m core data predicate
https://youtu.be/hz1h_ColGy0?t=14m45s array sort (using ‘compare’ method )
https://youtu.be/hz1h_ColGy0?t=17m10s collection view controller에서 cell을 선택한 경우 다른 controller로 연결되게 하는 작업 (programticallay)
https://youtu.be/hz1h_ColGy0?t=20m10s collection view 내용, 모양 변경 (programmatically)
Core Data and Swift: Relationships and More Fetching
original source : https://code.tutsplus.com/tutorials/core-data-and-swift-relationships-and-more-fetching–cms-25070
In the previous article, we learned about NSManagedObject
and how easy it is to create, read, update, and delete records using Core Data. However, I didn’t mention relationships in that discussion. Aside from a few caveats you need to be aware of, relationships are just as easy to manipulate as attributes. In this article, we will focus on relationships and we’ll also continue our exploration of NSFetchRequest
.
Prerequisites
What I cover in this series on Core Data is applicable to iOS 7+ and OS X 10.10+, but the focus will be on iOS. In this series, I will work with Xcode 7.1 and Swift 2.1. If you prefer Objective-C, then I recommend reading my earlier series on the Core Data framework.
1. Relationships
We’ve already worked with relationships in the Core Data model editor and what I’m about to tell you will therefore sound familiar. Relationships are, just like attributes, accessed using key-value coding. Remember that the data model we created earlier in this series defines a Person entity and an Address entity. A person is linked to one or more addresses and an address is linked to one or more persons. This is a many-to-many relationship.
To fetch the addresses of a person, we simply invoke valueForKey(_:)
on the person, an instance of NSManagedObject
, and pass in addresses as the key. Note that addresses is the key we defined in the data model. What type of object do you expect? Most people new to Core Data expect a sorted array, but Core Data returns a set, which is unsorted. Working with sets has its advantages as you’ll learn later.
Creating Records
Enough with the theory, open the project from the previous article or clone it from GitHub. Let’s start by creating a person and then link it to an address. To create a person, open AppDelegate.swift and update application(_:didFinishLaunchingWithOptions:)
as shown below.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Create Person
let entityPerson = NSEntityDescription.entityForName(“Person”, inManagedObjectContext: self.managedObjectContext)
let newPerson = NSManagedObject(entity: entityPerson!, insertIntoManagedObjectContext: self.managedObjectContext)
// Populate Person
newPerson.setValue(“Bart”, forKey: “first”)
newPerson.setValue(“Jacobs”, forKey: “last”)
newPerson.setValue(44, forKey: “age”)
return true
}
This should look familiar if you’ve read the previous article. Creating an address looks similar as you can see below.
// Create Address
let entityAddress = NSEntityDescription.entityForName(“Address”, inManagedObjectContext: self.managedObjectContext)
let newAddress = NSManagedObject(entity: entityAddress!, insertIntoManagedObjectContext: self.managedObjectContext)
// Populate Address
newAddress.setValue(“Main Street”, forKey: “street”)
newAddress.setValue(“Boston”, forKey: “city”)
Because every attribute of the Address entity is marked as optional, we don’t need to assign a value to each attribute. In the example, we only set the record’s street and city attributes.
Creating a Relationship
To link newAddress
to newPerson
, we invoke valueForKey(_:)
, passing in addresses
as the key. The value that we pass in is an NSSet
instance that contains newAddress
. Take a look at the following code block for clarification.
// Add Address to Person
newPerson.setValue(NSSet(object: newAddress), forKey: “addresses”)
do {
try newPerson.managedObjectContext?.save()
} catch {
let saveError = error as NSError
print(saveError)
}
We call save()
on the managed object context of newPerson
to propagate the changes to the persistent store. Remember that calling save()
on a managed object context saves the state of the managed object context. This means that newAddress
is also written to the backing store as well as the relationships we just defined.
You may be wondering why we didn’t link newPerson
to newAddress
, because we did define an inverse relationship in the data model. Core Data creates this relationship for us. If a relationship has an inverse relationship, then Core Data takes care of this automatically. You can verify this by asking newAddress
for its persons
.
Fetching and Updating a Relationship
Updating a relationship isn’t difficult either. The only caveat is that we need to add or remove elements from the immutable NSSet
instance Core Data hands to us. To make this task easier, however, the NSKeyValueCoding
protocol declares a convenience method mutableSetValueForKey(_:)
, which returns an NSMutableSet
object. We can then simply add or remove an item from the collection to update the relationship.
Take a look at the following code block in which we create another address and associate it with newPerson
. We do this by invoking mutableSetValueForKey(_:)
on newPerson
and adding otherAddress
to the mutable set. There is no need to tell Core Data that we’ve updated the relationship. Core Data keeps track of the mutable set that it gave us and updates the relationship.
// Create Address
let otherAddress = NSManagedObject(entity: entityAddress!, insertIntoManagedObjectContext: self.managedObjectContext)
// Set First and Last Name
otherAddress.setValue(“5th Avenue”, forKey:“street”)
otherAddress.setValue(“New York”, forKey:“city”)
// Add Address to Person
let addresses = newPerson.mutableSetValueForKey(“addresses”)
addresses.addObject(otherAddress)
Deleting a Relationship
You can delete a relationship by invoking setValue(_:forKey:)
, passing in nil
as the value and the name of the relationship as the key. In the next code snippet, we unlink every address from newPerson
.
// Delete Relationship
newPerson.setValue(nil, forKey:“addresses”)
2. One-To-One and One-To-Many Relationships
One-To-One Relationships
Even though our data model doesn’t define a one-to-one relationship, you’ve learned everything you need to know to work with this type of relationship. Working with a one-to-one relationship is identical to working with attributes. The only difference is that the value you get back from valueForKey(_:)
and the value you pass to setValue(_:forKey:)
is an NSManagedObject
instance.
Let’s update the data model to illustrate this. Open Core_Data.xcdatamodeld and select the Person entity. Create a new relationship and name it spouse. Set the Person entity as the destination and set the spouse relationship as the inverse relationship.

As you can see, it’s possible to create a relationship in which the destination of the relationship is the same entity as the entity that defines the relationship. Also note that we always set the inverse of the relationship. As the Apple’s documentation states, there are very few situations in which you want to create a relationship that doesn’t have an inverse relationship.
Do you know what will happen if you were to build and run the application? That’s right, the application would crash. Because we changed the data model, the existing backing store, a SQLite database in this example, is no longer compatible with the data model. To remedy this, remove the application from your device or the simulator and run the application. Don’t worry though, we’ll solve this problem more elegantly in a future installment using migrations.
If you can run the application without problems, then it’s time for the next step. Head back to the application delegate class and add the following code block.
// Create Another Person
let anotherPerson = NSManagedObject(entity: entityPerson!, insertIntoManagedObjectContext: self.managedObjectContext)
// Set First and Last Name
anotherPerson.setValue(“Jane”, forKey: “first”)
anotherPerson.setValue(“Doe”, forKey: “last”)
anotherPerson.setValue(42, forKey: “age”)
To set anotherPerson
as the spouse of newPerson
, we invoke setValue(_:forKey:)
on newPerson
and pass in anotherPerson
and "spouse"
as the arguments. We can achieve the same result by invoking setValue(_:forKey:)
on anotherPerson
, passing in newPerson
and "spouse"
as the arguments.
// Create Relationship
newPerson.setValue(anotherPerson, forKey: “spouse”)
One-To-Many Relationships
Let’s finish with a look at one-to-many relationships. Open Core_Data.xcdatamodeld, select the Person entity, and create a relationship named children. Set the destination to Person, set the type to To Many, and leave the inverse relationship empty for now.

Create another relationship named father, set the destination to Person, and set the inverse relationship to children. This will automatically populate the inverse relationship of the children relationship we left blank a moment ago. We’ve now created a one-to-many relationship, that is, a father can have many children, but a child can only have one biological father.

Head back to the application delegate and add the following code block. We create another Person record, set its attributes, and set it as a child of newPerson
by asking Core Data for a mutable set for the key children and adding the new record to the mutable set.
// Create a Child Person
let newChildPerson = NSManagedObject(entity: entityPerson!, insertIntoManagedObjectContext: self.managedObjectContext)
// Set First and Last Name
newChildPerson.setValue(“Jim”, forKey: “first”)
newChildPerson.setValue(“Doe”, forKey: “last”)
newChildPerson.setValue(21, forKey: “age”)
// Create Relationship
let children = newPerson.mutableSetValueForKey(“children”)
children.addObject(newChildPerson)
The following code block accomplishes the same result by setting the father
attribute of anotherChildPerson
. The result is that newPerson
becomes the father of anotherChildPerson
and anotherChildPerson
becomes a child of newPerson
.
// Create Another Child Person
let anotherChildPerson = NSManagedObject(entity: entityPerson!, insertIntoManagedObjectContext: self.managedObjectContext)
// Set First and Last Name
anotherChildPerson.setValue(“Lucy”, forKey: “first”)
anotherChildPerson.setValue(“Doe”, forKey: “last”)
anotherChildPerson.setValue(19, forKey: “age”)
// Create Relationship
anotherChildPerson.setValue(newPerson, forKey: “father”)
3. More Fetching
The data model of our sample application has grown quite a bit in terms of complexity. We’ve created one-to-one, one-to-many, and many-to-many relationships. We’ve seen how easy it is to create records, including relationships. If we also want to be able to pull that data from the persistent store, then we need to learn more about fetching. Let’s start with a simple example in which we see how to sort the results returned by a fetch request.
Sort Descriptors
To sort the records we get back from the managed object context, we use the NSSortDescriptor
class. Take a look at the following code snippet.
// Create Fetch Request
let fetchRequest = NSFetchRequest(entityName: “Person”)
// Add Sort Descriptor
let sortDescriptor = NSSortDescriptor(key: “first”, ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
// Execute Fetch Request
do {
let result = try self.managedObjectContext.executeFetchRequest(fetchRequest)
for managedObject in result {
if let first = managedObject.valueForKey(“first”), last = managedObject.valueForKey(“last”) {
print(“(first) (last)”)
}
}
} catch {
let fetchError = error as NSError
print(fetchError)
}
We initialize a fetch request by passing in the entity that we’re interested in, Person. We then create an NSSortDescriptor
object by invoking init(key:ascending:)
, passing in the attribute of the entity we’d like to sort by, first, and a boolean indicating whether the records need to be sorted in ascending or descending order.
We tie the sort descriptor to the fetch request by setting the sortDescriptors
property of the fetch request. Because the sortDescriptors
property is of type [NSSortDescriptor]?
, it is possible to specify more than one sort descriptor. We’ll take a look at this option in a moment.
The rest of the code block should look familiar. The fetch request is passed to the managed object context, which executes the fetch request when we invoke executeFetchRequest(_:)
. Remember that the latter is a throwing method, which means that we use the try
keyword and execute the fetch request in a do-catch
statement.
Run the application and inspect the output in Xcode’s console. The output should look similar to what is shown below. As you can see, the records are sorted by their first name.
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px ‘Helvetica Neue’; color: #454545}
Bart Jacobs
Jane Doe
Jim Doe
Lucy Doe
If you see duplicates in the output, then make sure to comment out the code we wrote earlier to create the records. Every time you run the application, the same records are created, resulting in duplicate records.
Like I mentioned, it is possible to combine multiple sort descriptors. Let’s sort the records by their last name and age. We first set the key of the first sort descriptor to last. We then create another sort descriptor with a key of age and add it to the array of sort descriptors.
// Add Sort Descriptor
let sortDescriptor1 = NSSortDescriptor(key: “last”, ascending: true)
let sortDescriptor2 = NSSortDescriptor(key: “age”, ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor1, sortDescriptor2]
fetchRequest.sortDescriptors = [sortDescriptor1, sortDescriptor2]
The output shows that the order of the sort descriptors in the array is important. The records are first sorted by their last name and then by their age.
Lucy Doe (19)
Jim Doe (21)
Jane Doe (42)
Bart Jacobs (44)
Predicates
Sort descriptors are great and easy to use, but predicates are what really makes fetching powerful in Core Data. Sort descriptors tell Core Data how the records need to be sorted. Predicates tell Core Data what records you’re interested in. The class we’ll be working with is NSPredicate
.
Let’s start by fetching every member of the Doe family. This is very easy to do and the syntax will remind some of you of SQL.
// Fetching
let fetchRequest = NSFetchRequest(entityName: “Person”)
// Create Predicate
let predicate = NSPredicate(format: “%K == %@”, “last”, “Doe”)
fetchRequest.predicate = predicate
// Add Sort Descriptor
let sortDescriptor1 = NSSortDescriptor(key: “last”, ascending: true)
let sortDescriptor2 = NSSortDescriptor(key: “age”, ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor1, sortDescriptor2]
// Execute Fetch Request
do {
let result = try self.managedObjectContext.executeFetchRequest(fetchRequest)
for managedObject in result {
if let first = managedObject.valueForKey(“first”), last = managedObject.valueForKey(“last”), age = managedObject.valueForKey(“age”) {
print(“(first) (last) ((age))”)
}
}
} catch {
let fetchError = error as NSError
print(fetchError)
}
We haven’t changed much apart from creating an NSPredicate
object by invoking init(format:arguments:)
and tying the predicate to the fetch request by setting the latter’s predicate
property. Note that the init(format:arguments:)
method accepts a variable number of arguments.
The predicate format string uses %K
for the property name and %@
for the value. As stated in the Predicate Programming Guide, %K
is a variable argument substitution for a key path while %@
is a variable argument substitution for an object value. This means that the predicate format string of our example evaluates to last == "Doe"
.
If you run the application and inspect the output in Xcode’s console, you should see the following result:
Lucy Doe (19)
Jim Doe (21)
Jane Doe (42)
There are many operators we can use for comparison. In addition to =
and ==
, which are identical as far as Core Data is concerned, there’s also >=
and =>
, <=
and =>
, !=
and <>
, and >
and <
. I encourage you to experiment with these operators to learn how they affect the results of the fetch request.
The following predicate illustrates how we can use the >=
operator to only fetch Person records with an age attribute greater than 30.
let predicate = NSPredicate(format: “%K >= %i”, “age”, 30)
We also have operators for string comparison, CONTAINS
, LIKE
, MATCHES
, BEGINSWITH
, and ENDSWITH
. Let’s fetch every Person record whose name CONTAINS
the letter j
.
let predicate = NSPredicate(format: “%K CONTAINS %@”, “first”, “j”)
If you run the application, the array of results will be empty since the string comparison is case sensitive by default. We can change this by adding a modifier like so:
let predicate = NSPredicate(format: “%K CONTAINS[c] %@”, “first”, “j”)
You can also create compound predicates using the keywords AND
, OR
, and NOT
. In the following example, we fetch every person whose first name contains the letter j
and is younger than 30
.
let predicate = NSPredicate(format: “%K CONTAINS[c] %@ AND %K < %i”, “first”, “j”, “age”, 30)
Predicates also make it very easy to fetch records based on their relationship. In the following example, we fetch every person whose father’s name is equal to Bart
.
let predicate = NSPredicate(format: “%K == %@”, “father.first”, “Bart”)
The above predicate works as expected, because %K
is a variable argument substitution for a key path, not just a key.
What you need to remember is that predicates enable you to query the backing store without you knowing anything about the store. Even though the syntax of the predicate format string is reminiscent of SQL in some ways, it doesn’t matter if the backing store is a SQLite database or an in-memory store. This is a very powerful concept that isn’t unique to Core Data. Rails’s Active Record is another fine example of this paradigm.
There is much more to predicates than what I’ve shown you in this article. If you’d like to learn more about predicates, I suggest you take a peak at Apple’s Predicate Programming Guide. We’ll also work more with predicates in the next few articles of this series.
Conclusion
You now have a good grasp of the basics of Core Data and it’s time to start working with the framework by creating an application that leverages Core Data’s power. In the next article, we meet another important class of the Core Data framework, NSFetchedResultsController
. This class will help us manage a collection of records, but you’ll learn that it does quite a bit more than that.