M6L1-2 : Recipe App Enhanced with Core Data

Wow, that was pretty “interesting” to say the least!

After a brief introductory video (lesson 1), lesson 2 of module 6 of the CWC+ iOS Database Course takes us down the rocky road of modifying our old iOS Foundations Recipe App ready to use Core Data.

Before we start, there’s a hurdle. Chris provides a “starter project” of the Recipe App in case we don’t have a working version or in case we didn’t do that module. I have mine from before, complete with all of my notes, so that’s fine.

Except that it isn’t.

As part of the Recipe App module, we had a final “bonus” lesson in which we upgraded the Recipe App with a categories section. The “starter project” provided by Chris does not have this upgrade, so I’m already on the back foot.

Spot The Difference :

“Starter” project
“Upgraded” project

We need to make quite a number of changes to our app, first by adding entities and attributes, then having to change some of our Recipe model – except that Chris’ Recipe model is not the same as ours (because there’s no “category” property in his model).

Commenting out this property is the first step, except when you realise just how much of our view code we altered to use the category property. Commenting out that lot is another slog… There’s even a whole RecipeCategoryView.swift file that’s absent from Chris’ project. Everywhere that’s called up now also needs commenting out.

We also have a Constants.swift file that’s not present in Chris’ version. That needs to go.

Our JSON datafile has many entries for “category” as well. I’m hoping I won’t need to wade through that and delete them, too.

There’s also a lack of id: \.self in Chris’ ForEach code. This is something we stumbled across before and, if we just used Chris’ “starter project” verbatim, we’d also be seeing all of those old warning errors again.

Only when this is all done can we continue with the lesson in the hope that we’re on the same page as Chris.

Images from binary Core Data

The lesson does provide us with some very valuable information – and that’s the changes made to displaying images.

Previously in the app, we just used code such as:

Image(recipe.image)
    .resizable()
    .scaledToFill()

That would take the image from the asset library, but we’re now going to be storing the image as binary data in Core Data, so we need to modify it to this code:

let image = UIImage(data: recipe.image ?? Data()) ?? UIImage()
Image(uiImage: image)
    .resizable()
    .scaledToFill()

All good.

Xcode’s Wobbly…

After making a good number of changes to our app, Xcode refuses to build with the rather obscure error:

The compiler is unable to type-check this expression in reason time; try breaking up the expression into distinct sub-expressions.

That’s a fairly meaningless error, which Chris solved by using his experience to determine what might cause the problem. One cause was that our ingredients propery of the new core data Recipe extension is now of type NSSet. I’m not entirely sure what that means, but the solution is to change code like this:

ForEach (recipe.ingredients) { item in 
    ...
}

to this:

ForEach (recipe.ingredients.allObjects as! [Ingredient]) { item in 
    ...
}

We also had some minor glitches caused by some of our properties previously being optional, and now they’re not going to be optional. But that’s all par for the course compared to everything else.

In the end, Chris resolved all of his errors in the video but my Xcode continued being a stubborn little thing and kept failing my build. My solution was to compare my files to Chris’ Lesson 2 solution project using WinMerge on my Windows computer. This enabled me to compare the files side-by-side to see if I had made a silly error but, no, the only real difference was all of the commented out notes and commented out “category” elements.

Whilst comparing the files, I just took a bit of time to move some of my in-line comments to the next line down whilst occasionally pressing CMD+B to try and build the project. Before I’d finished this on all the files, I suddenly noticed that Xcode had done a “successful” build – so it was now all working okay. I hadn’t actually changed any code, and I sure hadn’t “broken up the expression” as its error message suggested.

But, anyway, it enabled me to continue with the lesson in which Chris takes us through creating a new project to grab the core data Persistence.swift file, make a couple of changes for our app, and update our app entry point with the appropriate code, re:

import SwiftUI

@main
struct EnhancedRecipeApp: App {
    let persistenceController = PersistenceController.shared

    var body: some Scene {
        WindowGroup {
            RecipeTabView()
                .environment(\.managedObjectContext, persistenceController.container.viewContext)
        }
    }
}

This is the same as what we previously learned in lesson 3 of module 5, so we’re already getting familiar with some of this mire of CRUD (pun most definitely intended!).

Now, we’re ready to move on…