Core Data: Beyond the Basics

Jul 26 2022 · Swift 5.5, iOS 15, Xcode 13.3.1

Part 1: Fetching & Displaying Launches

06. Modeling Relationships

Episode complete

Play next episode

Next
About this episode

Leave a rating/review

See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 05. Dynamically Adjust Sort Descriptors Next episode: 07. Adding Launches to Lists

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Personal Plan. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

Transcript: 06. Modeling Relationships

Your RocketLaunch app is coming along quite nicely - you can save rocket launches and mark them as viewed. But there is a pretty big limitation with the app - all launches are added to a single list. In Apple’s Reminders app, for example, you can create multiple lists each with its own set of Reminders. Let’s do the same.

Navigate to the data model editor. In here you’re going to add another entity to model a list. Tap on Add Entity to add another one and in the inspector change the name to RocketLaunchList. Ideally you would name this just List, but there’s already a List type in SwiftUI and you’ll get conflicts.

A RocketLaunchList will have a single attribute, a title of type String, that is non optional. You might be thinking - don’t you need to add an attribute named launches? When using Core Data if you want to represent a connection between two different entities you define a relationship instead.

Right under the attributes section you should see a section named Relationships. Tap on the plus button to add a new one.

Like Core Data attributes you need to configure different aspects of your relationships and you start with a name. Give this relationship the name launches.

Next up is the destination of this relationship. If you think of the RocketLaunchList as the source of the relationship, the destination is the entity you’re creating a relationship with, in this case that is the RocketLaunch entity.

Leave the inverse column empty for now. Next, you’re going to move to the Data Model Inspector where you can customize this relationship further.

First up, should this relationship be optional? That is should every list have a launch? You can have an empty list, like when you first create it, or when you view all launches in the list; so let’s leave this as optional.

You’ve already specified the Destination property and you’re going to skip the next two for now. The last attribute here is what type of relationship this is. Is this a “To One” relationship - that is, can one launch list have only one launch, or is it a “To Many”, where one launch list can have more than one launch.

It’s definitely the latter, so let’s change that to To Many. You’ll notice that when you do that, additional options appear in the inspector. You can specify if this relationship is ordered and whether you can have a minimum or maximum number of launches stored in a list.

Let’s go ahead and mark this as ordered. Doing so affects the underlying type when the relationship is defined in code - instead of a set you end up using an ordered set, which is an Objective-C type available in the Foundation library.

Now switch over to the RocketLaunch entity. When defining relationships Core Data recommends that you define the relationship in both directions. We just defined a relationship from RocketLaunchList to RocketLaunch - let’s add the inverse, from the perspective of the RocketLaunch.

In the RocketLaunch entity, tap on the plus button in the Relationship section to add a new relationship. You’re going to name this list since the inverse is that every RocketLaunch should belong to a RocketLaunchList.

The destination for this relationship is a RocketLaunchList and this time around you can specify the relationship you defined earlier as the inverse. From the dropdown select launches.

Switch over to the Data Model Inspector. Unlike the previous relationship you defined, in this case every RocketLaunch should be associated with a RocketLaunchList. We don’t want to have these standalone launches that you can add outside of lists. Uncheck the optional checkbox.

That’s the only change you’re going to make here because the defaults are what you want. A launch can only belong to one list and so this is a To One relationship.

That should be all you need to model. If you switch back over to RocketLaunchList you’ll see that the inverse property on the relationship has been updated to reflect the one you just defined.

Now that you have a new Entity, you need to model it in code. Unlike the last time, you’re going to let Core Data handle this for you. Navigate to the Editor menu and select Create NSManagedObject Subclass.

On the modal that appears, select the RocketLaunches model and then subsequently the RocketLaunchList. Note that RocketLaunch has not been selected for you because you let Xcode know that you were manually generating the code for this Entity.

Hit Next and make sure you add it both to the Model folder in the file directory and the group in Xcode. Hit create.

Like the pattern you followed earlier you’ll see that two files get added to the Model group - RocketLaunchList+CoreDataClass containing a simple class definition and RocketLaunchList+CoreDataProperties with all the extra stuff you need to use the class.

There’s a lot more going on in this file than you added to the RocketLaunch extension earlier. For starters you have a basic fetch request defined right out of the box. There’s noting different here compared to the one you wrote except for the fact that this function is annotated with @nonobjc.

If you haven’t used this keyword before its essentially the opposite of @objc and hides this Swift declaration from Objective-C. Generic fetch requests are a very Swift thing and by default Core Data doesn’t expose this method to Objective-C code.

You’ll also notice that title is marked as an optional even though you specifically said it shouldn’t be optional. That’s just a Core Data quirk we talked about earlier.

Right below that is the property that was generated for the launches relationship. Notice that the type of this set is NSOrderedSet which is an Objective-C type. NSOrderedSet is not generic and if you were to iterate over this the resulting values would be of type AnyObject, which is not at all convenient.

Change this to a non optional Array containing RocketLaunches.

@NSManaged public var launches: [RocketLaunch]

There are also accessors below that have been generated. You won’t be using them, so you can remove them if you want. If you try and build here it will immediately fail and that’s because you modified the generated code without switching the codegen to Manual/None. Go ahead and do that for the RocketLaunchList type.

Next let’s define a method to easily create a RocketLaunchList in RocketLaunchList+CoreDataProperties.swift:

static func create(withTitle title: String, in managedObjectContext: NSManagedObjectContext) {
	let newLaunchList = self.init(context: managedObjectContext)
	newLaunchList.title = title
	
	do {
	  try managedObjectContext.save()
	} catch {
	  let nserror = error as NSError
	  fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
	}
}

Great, now just like RocketLaunch you can also create and save RocketLaunchLists very easily.

One more thing before you take a break. If you navigate back to the data model editor, you’ve been working in the Table editor style. As your object graph grows larger it often helps to look at it in the Graph style editor. On the bottom right, click on the Graph button to switch the editor style and you should see your object graph along with the relationships between them.

Build and run to make sure everything works. You might also need to do a clean build first using Cmd+K. Let’s take a quick break here. In the next video you’ll incorporate RocketLaunchLists into the app.