Mastering In CoreData (Part 9 NSFetchRequest)
NSFetchRequest is use to access the existing data. The fetching of objects from Core Data is one of the most powerful features of this framework. It defines criteria to search/query/retrieve data from the cache (NSManagedObjectContext) or persistent store.
We used fetch request in earlier tutorials. See part 4 to part 8 we fetched objects using NSFetchRequest.
Explanation
Figure 1, A fetch request tells a Managed Object Context the entity of the Managed Objects that you want to fetch; optionally, it specifies other aspects such as constraints on the values the objects’ properties must have and the order you want the objects returned in.
A fetch request is an instance of NSFetchRequest. The entity it specifies is represented by an instance of NSEntityDescription; any constraints are represented by an NSPredicate object, and the ordering by an array of one or more instances of NSSortDescriptor. These are akin to the table name, WHERE clause, and ORDER BY clauses of a database SELECT statement respectively.
You execute a fetch request by sending a message to a managed object context. The context returns an array containing the objects (if any) that matched the request
Getting Started
You can Download the starter project here. In part 5 we discussed validation so comment out “User+CoreDataValidations.swift” file completely since it will create a problem and if you are following previous tutorials delete the application first .
Fetching NSManagedObject instances
Go to xcdatamodel → Tap on User Entity → See properties we added previously as shown in Figure 2
The process of fetching the records from Core Data has following tasks as shown in Figure 3
- Refer to persistentContainer from appdelegate singleton object
- Create/Access the singleton managed object context from persistentContainer
- Created a fetch request to filter only NSManagedObject having entity name User. This example does not add any requirements to that data other than the type of entity being returned
- You handed the fetch request to the managed object context to do the heavy lifting. fetch(_:) returns an array of managed objects meeting the criteria specified by the fetch request. Method has two possible results. It either returns an NSArray object of type NSManagedObject with zero or more objects, or it throw an error , you have received an error from Core Data and need to respond to it
Filtering
NSPredicate object is used to fetch request to narrow/filtered the number of objects being returned. For example, if you only want User objects that have a firstName = ali, you add the predicate directly to
NSFetchRequest
To illustrate this example, let’s dive into the code. First comment out “User+CoreDataValidations” file so that no more validation will happen.
In the Figure 4 we did a couple of things
- Firstly we added two users
- Save objects into the persistent store by calling save method
Now we need to filters User objects having firstName = “ali” and it return one user only as shown below in the Figure 5.
Sorting
NSSortDescriptor object is used to order/sort a collection of objects fetched by the NSFetchRequest based on a property common to all the objects. For example, if you want all User objects sorted by firstName in an ascending order , you add the NSSortDescriptor instance directly to NSFetchRequest
To illustrate this example, let’s dive into the code. Delete the application first. As you can see in the Figure 6 we created three users with different firstName and secondName
As you can see in the Figure 7 we did a number of things
- First, we created two sort Descriptors and the highest priority was given firstName property of User after that secondName will considered
- Printed in the console and the result now sorted as shown in the console of Figure 7
Optimization
In this section we will look what features NSFetchRequest provided to improve the fetching time response and memory consumption
Which Persistent Store to Search
In part 2 Core Data stack part we said under the Persistent store heading that :
We can make multiple persistent stores per stack
The fetch request can be modified to search particular stores using the setAffectedStores: method on an NSFetchRequest.
When you’re creating an object, you can assign the entity to a particular store using the assignObject:toPersisentStore: method on NSManagedObjectContext. As you can see in Figure 8 we assigned User, Passport, and Task Entities to Persistent Store One
A fetch request fetched data from a persistent store. Most of the application have only one persistent store but there are some application that can have multiple persistent stores. NSFetchRequest has affectedStores property which takes array of persistent stores and when you execute this fetch request it will only search on these persistent stores. As you can see in Figure 8 we told fetch request to look only Persistent Store One
[request setAffectedStores:[NSArray arrayWithObjects:firstStore,secondStore,thirdStore, nil]];
This assumption is not tested
- When your application have multiple persistent stores and you don’t specify affectedStores property, it will look from all the persistent stores
- If you specify this property it will look only these stores
As you can see in Figure 8. You can query on the store that contain User entity only and it surely increases the performance of fetch results.
Result type of the Fetch Request
You can set the fetch Request result. As we know that NSFetchRequest always return Array but the type of array is decided by the result type. There are currently four result type supported by NSFetchrequest which are listed below
- managedObjectResultType (Default )
- dictionaryResultType. (Return array of dictionary)
- countResultType. (Return only count )
- managedObjectIDResultType (Return only objectIds )
As you see in Figure 10, We used managedObjectResultType and for that we performed number of tasks
- First we created a fetch request with array type User
- Second we defines the result type with “managedObjectResultType” and it is a default type, which we expect from fetchrequest (make sure step 1 and step 2 should be consistent otherwise app will crash).Majority of the case we needed this type that’s why it is the default one
- Printed User object as you can see User whole objects is printed on the console in Figure 9
As you see in Figure 10, We used dictionaryResultType and for that we performed number of tasks
- First we created a fetch request with array type NSDictionary
- Second we defines the result type with “dictionaryResultType” , which we expect from fetchrequest (make sure step 1 and step 2 should be consistent otherwise app will crash).
- Printed User details using their property which acts as a key in Dictionary as shown in Figure 10
As you see in Figure 11, We used countResultType and for that we performed number of tasks
- First we created a fetch request with array type NSNumber
- Second we defines the result type with “countResultType” , which we expect from fetchrequest (make sure step 1 and step 2 should be consistent otherwise app will crash).
- Printed User users count in the database as shown in Figure 10. As you can see sometimes we need total count only instead of loading whole objects in the memory we can used this which surely reduces memory consumption
As you see in Figure 12, We used managedObjectIDResultType and for that we performed number of tasks
- First we created a fetch request with array type NSManagedObjectID
- Second we defines the result type with “managedObjectIDResultType” , which we expect from fetchrequest (make sure step 1 and step 2 should be consistent otherwise app will crash).
- Printed User Object Id which is unique for every record in the context. The question that might be arised in your mind what is NSManagedObjectID we will look into this when we will be doing multiple managedObjectContext or in threading part as well . It’s not very helpful when we have only one NSManagedObjectContext. At this moment if you don’t understand anything about it it’s OK but one think you should take is that we can return NSMangedObjectId in fetch request as well by changing it’s resultType to NSManagedObjectID. It will surely optimize the performance when we need to transfer data between two contexts.
Summary
In this part 9 we looked NSFetchRequest in depth with coding and diagrams and also looked how can we improve memory and time using its hidden features.
What Next?
This tutorials was getting bigger so I decided to break this into other part . So in part 10 we will look other features and optimization techniques that NSFetchRequest supports
Useful Links
https://developer.apple.com/documentation/coredata/nsfetchrequest
https://developer.apple.com/library/archive/documentation/DataManagement/Devpedia-CoreData/fetchRequest.html#//apple_ref/doc/uid/TP40010398-CH26-SW1
https://developer.apple.com/documentation/coredata/nsfetchrequest/1506518-affectedstores