KEMBAR78
Fetching Records With Core Data | PDF | Swift (Programming Language) | Method (Computer Programming)
0% found this document useful (0 votes)
17 views2 pages

Fetching Records With Core Data

Core Data tutorial

Uploaded by

afonjajim
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
17 views2 pages

Fetching Records With Core Data

Core Data tutorial

Uploaded by

afonjajim
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 2

What's New Library Search Sign In

Fetching Records With Core Data


Fetching Records With Core Data: Type
Methods
by Bart Jacobs in Core Data
Swift 4 Xcode 9 iOS 11

Fetching Records With Core Data


1 Fetching Records With Core Data: Type Methods

2 Type Methods 08:43

Featured

Samsara 6 Is Here!
Samsara is a minimalist timer for yoga, meditation, and
mindfulness. It sports a simple design that lets you focus on what
matters, your practice. Over the past ten years, it has become the
companion of thousands of people around the world. Download
Samsara for free from the App Store.

Developers often complain that Core Data has an arcane syntax and a complicated

API. "It's tedious to work with Core Data." seems to be the general consensus. It's true

that Core Data used to be difficult to use and the framework's syntax wasn't as

elegant as it could be. That's something of the past, though. The more Core Data

matures, the more I enjoy and appreciate the framework.

First impressions are difficult to change and it's therefore unsurprising that

developers often fall back on third party libraries. Using a third party library to

interact with a first party framework isn't something I recommend.

Many of us find fetching records from a persistent store to be clunky and tedious. Is

that true? In this series, I'd like to show you how easy and elegant fetching records

from a persistent store can be.

We start with a simple example every developer familiar with Core Data

understands. Along the way, we add more complexity by introducing flexibility and

dynamism. We also leverage generics to make sure we don't unnecessarily repeat

ourselves. Core Data and generics play very well together. Let's start with a simple

data model.

Setting the Stage


Setting Up the Project
Launch Xcode and create a project based on the Single View App template.

Let's assume we're creating an application that tracks the user's workouts. Name the

project Workouts and, to save a bit of time, check the Use Core Data checkbox at

the bottom.

Chooseoptionsforyournewproject:

ProductName: Workouts

Team: BartJacobs

OrganizationName: Cocoacasts

OrganizationIdentifier: com.cocoacasts

BundleIdentifier:com.cocoacasts.Workouts

Language:Swift
/UseCoreData
•IncludeUnitTests
IncludeUlTests

Cancel Previous Next

Populating the Data Model


The data model isn't too complicated. This is what it looks like. We define three

entities, Workout, Exercise, and Session.

As you can see, a workout has a name and it's linked to zero or more exercises. An

exercise can belong to only one workout. Completed workouts are saved as sessions

hence the Session entity. A session stores the duration of the workout and it also

keeps a reference to the workout.

Every entity I create defines three default attributes:

uuid of type String


createdAt of type Date
updatedAt of type Date

If your application's deployment target is iOS 11 or higher, you can use the brand new

UUID attribute type for the uuid attribute.

This is a summary of the entities of the data model.

Workout

Attributes - name of type String - uuid of type String - createdAt of type Date -

updatedAt of type Date

Relationships - sessions with Session as its destination - exercises with Exercise as

its destination

Exercise

Attributes - name of type String - uuid of type String - createdAt of type Date -

updatedAt of type Date - duration of type Double - order of type Integer 16

Relationships - workout with Workout as its destination

Session

ENTITIES VAttributes
E)Exercise
ESession Entity Attribute^ Type
E)Workout Session DcreatedAt Date
Session DupdatedAt Date
FETCHREQUESTS Session Suuid String
CONFIGURATIONS +-
©Default
Relationships
Entity Relationship^ Destination Inverse
Session ®workout Workout ^sessions^

+-
FetchedProperties
Entity FetchedPropertya Predicate

+-

Attributes - uuid of type String - createdAt of type Date - updatedAt of type Date

Relationships - workout with Workout as its destination

Xcode automatically creates a class definition for us. Why is that? Open the data

model by selecting it in the Project Navigator. Select the Workout entity and open

the Data Model Inspector on the right. Notice that Codegen in the Class section is

set to Class Definition. This means that Xcode generates a class definition for us. It's

the default setting in Xcode 9 at the time of writing.

First Things First


Fetching data from a persistent store isn't difficult. Let me show you what it looks

like. Before we start, though, we need to inject the managed object context of the

persistent container in the root view controller of the window. Euh ... what? Don't

worry. It's easy. This is what that looks like.

func application
application((_ application
application:: UIApplication
UIApplication,, didFinishLaunchingWithOptions launchOptions
launchOptions:: [ UIApplicationLaunchOptionsKey

// Load Storybaord
let storyboard = UIStoryboard
UIStoryboard((name
name:: "Main"
"Main",, bundle
bundle:: Bundle
Bundle..main
main))

// Instantiate Initial View Controller


guard let viewController = storyboard
storyboard..instantiateInitialViewController
instantiateInitialViewController(() as
as?? ViewController
fatalError
fatalError(()
}

// Configure View Controller


viewController
viewController..managedObjectContext = persistentContainer
persistentContainer..viewContext

// Configure Window
window
window??. rootViewController = viewController

return true
}

In the application(_:didFinishLaunchingWithOptions:) method of the AppDelegate class, we

instantiate the initial view controller of the main storyboard, an instance of the

ViewController class. We inject the managed object context of the persistent

container by setting the managedObjectContext property of the view controller. This only

works if we set the ViewController instance as the root view controller of the

application's window.

Before we can build and run the application, we need to declare a variable property,

managedObjectContext , of type NSManagedObjectContext! in the ViewController class. To make

sure we didn't make any mistakes, add a print statement in the view controller's

viewDidLoad() method and run the application.

import UIKit
import CoreData

class ViewController
ViewController:: UIViewController {

// MARK: - Properties

var managedObjectContext
managedObjectContext:: NSManagedObjectContext
NSManagedObjectContext!!

// MARK: - View Life Cycle

override func viewDidLoad


viewDidLoad(
() {
super
super..viewDidLoad
viewDidLoad(()

print
print((managedObjectContext
managedObjectContext))
}

This is a quick and dirty setup, but it's fine for what I'm about to show you. It's time to

fetch records.

Fetching Records
The persistent store is empty at the moment, but that isn't a problem for this

tutorial. The goal is to show you how to easily and elegantly fetch records from a

persistent store. Let's start with a simple fetch request.

In the viewDidLoad() method of the ViewController class, we create a fetch request by

invoking the fetchRequest() class method of the Workout class. Remember that Xcode

has generated the Workout class for us, a NSManagedObject subclass. The fetchRequest()

class method returns a NSFetchRequest<Workout> instance.

// MARK: - View Life Cycle

override func viewDidLoad


viewDidLoad(
() {
super
super..viewDidLoad
viewDidLoad(()

// Create Fetch Request


let fetchRequest
fetchRequest:: NSFetchRequest
NSFetchRequest<<Workout
Workout>> = Workout
Workout..fetchRequest
fetchRequest(()

do {
// Peform Fetch Request
let workouts = try managedObjectContext
managedObjectContext..fetch
fetch((fetchRequest
fetchRequest))

print
print((workouts
workouts))
} catch {
print
print(("Unable to Fetch Workouts, (
(\(
\(error
error)))"
)"))
}
}

We execute the fetch request by passing it to the fetch(_:) method of the

NSManagedObjectContext instance. The fetch(_:) method is throwing, which means we

need to wrap it in a do-catch statement and attach the try keyword to the method

invocation.

Because the persistent store is empty at the moment, the result printed to Xcode's

console is an empty array.

We've successfully executed a fetch request. However, by taking this approach to

interact with Core Data, the code you write is verbose and hard to test. The approach

I recommend is simpler and easier to test.

Type Methods
We start by creating a Swift file, Workout.swift, and define an extension for the

Workout class. Add an import statement for the Core Data framework at the top.

import CoreData

extension Workout {

I want to encapsulate fetching of Workout records in the Workout class. Let's start by

defining a class method, findAll(in:) that accepts a managed object context as its

only argument. The managed object context we pass to findAll(in:) is the one

executing the fetch request.

class func findAll


findAll((in managedObjectContext
managedObjectContext:: NSManagedObjectContext
NSManagedObjectContext)) - > [ Workout
Workout]] {

The implementation should look familiar. We create a fetch request and hand it to

the managed object context we pass to findAll(in:) .

class func findAll


findAll((in managedObjectContext
managedObjectContext:
: NSManagedObjectContext
NSManagedObjectContext)) - > [ Workout
Workout]] {
// Helpers
var workouts
workouts:: [ Workout
Workout]] = []

// Create Fetch Request


let fetchRequest
fetchRequest:: NSFetchRequest
NSFetchRequest<<Workout
Workout>> = Workout
Workout..fetchRequest
fetchRequest(()

do {
// Perform Fetch Request
workouts = try managedObjectContext
managedObjectContext..fetch
fetch((fetchRequest
fetchRequest))
} catch {
print
print(("Unable to Fetch Workouts, (
(\(
\(error
error)))"
)"))
}

return workouts
}

That's a good start, but there's room for improvement. Instead of handling the error

in the findAll(in:) method, it's more useful to propagate it to the method's call site.

This is easy to do by turning findAll(in:) into a throwing class method and omitting

the do-catch statement.

class func findAll


findAll((in managedObjectContext
managedObjectContext:
: NSManagedObjectContext
NSManagedObjectContext)) throws - > [ Workout
Workout]] {
// Helpers
var workouts
workouts:: [ Workout
Workout]] = []

// Create Fetch Request


let fetchRequest
fetchRequest:: NSFetchRequest
NSFetchRequest<<Workout
Workout>> = Workout
Workout..fetchRequest
fetchRequest(()

// Perform Fetch Request


workouts = try managedObjectContext
managedObjectContext..fetch
fetch((fetchRequest
fetchRequest))

return workouts
}

If this isn't what you want, then you can implement a variation that silences any

errors that are thrown. It looks something like this. Take a moment to understand

what's going on.

class func findAll


findAll((in managedObjectContext
managedObjectContext:: NSManagedObjectContext
NSManagedObjectContext)) - > [ Workout
Workout]] {
// Create Fetch Request
let fetchRequest
fetchRequest:: NSFetchRequest
NSFetchRequest<<Workout
Workout>> = Workout
Workout..fetchRequest
fetchRequest(()

return ( try
try?? managedObjectContext
managedObjectContext..fetch
fetch((fetchRequest
fetchRequest))) ? ? [ ]
}

If we use the try? keyword and an error is thrown, the error is handled by turning

the result into an optional value. This means that there's no need to wrap the

throwing method call in a do-catch statement. But notice that we don't return an

optional value as the result. If executing the fetch request fails for some reason, we

return an empty array.

I don't recommend ignoring errors and I therefore prefer the first implementation of

the findAll(in:) class method.

We can now update the implementation of the viewDidLoad() method of the

ViewController class. The result is cleaner and less verbose.

override func viewDidLoad


viewDidLoad(
() {
super
super..viewDidLoad
viewDidLoad(()

do {
// Fetch Workouts
let workouts = try Workout
Workout..findAll
findAll((in
in:: managedObjectContext
managedObjectContext))

print
print((workouts
workouts))
} catch {
print
print(("Unable to Fetch Workouts, (
(\(
\(error
error)))"
)"))
}
}

If we aren't interested in any errors that are thrown, we can still fall back to the try?

keyword. That's the benefit of choosing for a throwing method.

override func viewDidLoad


viewDidLoad(
() {
super
super..viewDidLoad
viewDidLoad(()

// Fetch Workouts
if let workouts = try
try?
? Workout
Workout..findAll
findAll((in
in:: managedObjectContext
managedObjectContext)) {
print
print((workouts
workouts))
} else {
print
print(("Unable to Fetch Workouts"
Workouts"))
}
}

By opting for the try? keyword, we don't ignore the error. It simply means that

we're not interested in the reason of the failed fetch request.

I frequently see developers print or log errors. I assume it gives them the feeling that

they're handling the error in some way. While this is helpful during development,

when things hit the fan an error is only useful if you take some action in response to

the error.

Adding Flexibility
I hope this tutorial has shown you that it's very easy to factor out a fetch request into

a class method. This makes it easier to test your code and it cleans up the call site

substantially.

In the next tutorial, we take it to the next level by adding more flexibility and

dynamism. We often need the ability to find specific records and it's also essential to

have the option to sort the results of the fetch request. This is something Core Data

needs to handle for us.


You might also like ...

Hard-Coding Seed Data

Type Methods

Asynchronous Fetching With Core Data And Operations

Versioning the Data Model

Welcome to Core Data Fundamentals

Next Episode "Type Methods"

Solutions Resources About Legal


Cocoacasts Plus Books Cocoacasts Privacy Policy
Mentorship Swift Patterns Testimonials Terms and Conditions
GitHub
Twitter

© Code Foundry 2016-2023

You might also like