Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

MacCheetah3

macrumors 68020
Original poster
Nov 14, 2003
2,435
1,363
USA
Background:
I'm new-ish to Swift and Apple ecosystem development, created two simple apps in Xcode, a change counter that I came up with, and one of the SwiftUI tutorials, Landmarks. I'm also nearing an AAS degree in computer programming.

I'm trying to create a recipe app -- well, more of a relaunch but that's not important -- using SwiftUI, CoreData, CloudKit, and other recent frameworks. And, quite frankly, I'm finding Apple's documentation lacking/confusing. Therefore, I'm hoping for someone to at least point me towards some good, basic examples and/or documentation/tutorials.

Sensibly, I want to take this on in testable steps. Implement the basic core storage and get it to display in the general format I desire -- finesse tweaking can happen later -- then additional views for search, then cloud syncing, and so on.

The Current Roadblock:
I've created a new project (Xcode 11.5), added an entity plus attributes, and now attempting to seed my storage, i.e., create records. I know of the PersistentContainer, NSManagedObjects, NSEntityDescription, NSManagedContext, AppDelegate, etc though can't seem to put them all together correctly.

Basically, I want a Swift version of
Code:
var taco = new Recipe
{
name: "Taco",
ingredients: "1 package of taco shells, 1 tomato, ...",
...
}
var burger = new Recipe
{
name: "Bean Burger",
...
}
Perhaps even as part of a Swift version of the following in an InitialReciipes function, in the main SwiftUI view, or wherever is best practice.
Code:
import CoreData

class ManageData
{
    var context = ...

    public void addRecipe
    {
       // insert into
    }
    ...
}

Again, I'm not asking for step-by-step instructions, more of an understandable, complete, basic, template would be nice, or at least an actually helpful tutorial.

Thanks.
 
Last edited:
[UPDATE] It seems the following is one of those Xcode glitches. I relaunched the app and the compiler is no longer identifying my class reference as a problem.

Nonetheless, I'm still open to suggestions for up-to-date tutorials.

--------------------------------------------------

Using this tutorial I recently found as a template, I (re)started my data management file with this:
Code:
import CoreData

public class DataManagement {

    let context: NSManagedObjectContext

    init(context: NSManagedObjectContext) {
        self.context = context
    }

    private func seedRecipes() {
        let recipes = [
            (name: "Toast", servings: "1 person"),
            (name: "Eggs", servings: "2 servings"),
            (name: "Ham", servings: "family"),
        ]

        for r in recipes {
            let newRecipe = NSEntityDescription.insertNewObject(forEntityName: "Recipe", into: context) as! RecipeMO
            newRecipe.name = r.name
            newRecipe.servings = r.servings
        }
    }
}
Xcode is giving me an error "Use of undeclared type 'RecipeMO'." I've verified the class name in the Model Editor is indeed RecipeMO and the model name is Recipe, the entity is set for the global namespace, and the code generation is class definition which I understand means Xcode auto-generates the class file.
 
Last edited:
I’ll try to write a more thorough answer a bit later but for now I’d recommend turning off class generation. In my experience it often creates issues like this one so it may fix your issue. Otherwise, when you select the “missing” file and check on the right side under “target membership” is there a checkbox for the relevant target that is ticked?
 
  • Like
Reactions: MacCheetah3
Thanks for the response. No huge rush, I'm doing this as a side project at this point -- and it's more frustrating then I expected.

I saw that Xcode allows me to write my own class (Manual/None code generation) or an extension/subclass (Category/Extension code generation) but after some contemplation decided, I hope that's not necessary. However, if you think it'd ultimately be less of a headache, I'm willing.

Would that also be the possible issue here?
Code:
import CoreData
import SwiftUI

struct ContentView: View {
    @FetchRequest(fetchRequest: RecipeMO.fetchRequest()) var recipes: FetchedResults<RecipeMO>

    var body: some View {
        List(recipes) {
            recipe in VStack(alignment: .leading) {
                recipe.name
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
I'm getting at least one error
Initializer 'init(_:rowContent:)' requires that 'FetchedResults<RecipeMO>.Element' (aka 'RecipeMO') conform to 'Identifiable'
Again, this all comes down to not having these classes, structs, functions, properties, and error not explained well enough -- unless I'm just not sifting through the Apple documentation properly.

P.S. I also saw that a person can create Fetch Requests in the Model Editor; is that useful?
P.P.S. Does the Relationship -> Type: To One/To Many apply to the selected model (one favorite to one recipe) or the model (one recipe to one favorite) it is referring to? -- these kinds of things I can't find answers or useful docs for. I also ask because I've considered having ingredients and instructions as separate models in one-to-many relationships.
P.P.P.S. Favorite is seperate because I was thinking that might be necessary when I finally get to CloudKit. I want the Recipes to be updated via the cloud and Favorites as optionally synced for the user on their iCloud space.
 

Attachments

  • Model_relationship.png
    Model_relationship.png
    82.1 KB · Views: 146
Last edited:
List in SwiftUI requires that each item in the collection fed to it conform to protocol Identifiable. It uses this to ensure each item is displayed once and re-use cells on scrolling etc. I guess RecipeMO does not have an id property? You could work round this using your own for each in the list and supplying and identifying path. Something like:

Code:
List {
ForEach(recipes, id:\.self) {
Text(recipe.name)
}
}
 
List in SwiftUI requires that each item in the collection fed to it conform to protocol Identifiable. It uses this to ensure each item is displayed once and re-use cells on scrolling etc. I guess RecipeMO does not have an id property? You could work round this using your own for each in the list and supplying and identifying path. Something like:

Code:
List {
ForEach(recipes, id:\.self) {
Text(recipe.name)
}
}
Thanks for the info. At least it's a real explanation.

I did see that in a Web search, including the workaround you posted of passing a second paramter. However, that didn't work, therefore, I added id: UUID to my model. Although -- maybe another Xcode not updating things as expected -- the compiler is still angry about not being identifiable.
 
It doesn’t sound like you’ve marked your model class as conforming to the Identifiable protocol. It would be nice if the compiler inferred protocols from conformance not it does not:

Code:
class DoesNotConformToIdentifiable {
let id = UUID()
}

class DoesConformToIdentifiable: Identifiable {
let id = UUID()
}
Despite both having identical id properties only one of these is Identifiable
 
  • Like
Reactions: MacCheetah3
Thanks for the response. No huge rush, I'm doing this as a side project at this point -- and it's more frustrating then I expected.

I saw that Xcode allows me to write my own class (Manual/None code generation) or an extension/subclass (Category/Extension code generation) but after some contemplation decided, I hope that's not necessary. However, if you think it'd ultimately be less of a headache, I'm willing.

Would that also be the possible issue here?
Code:
import CoreData
import SwiftUI

struct ContentView: View {
    @FetchRequest(fetchRequest: RecipeMO.fetchRequest()) var recipes: FetchedResults<RecipeMO>

    var body: some View {
        List(recipes) {
            recipe in VStack(alignment: .leading) {
                recipe.name
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
I'm getting at least one error

Again, this all comes down to not having these classes, structs, functions, properties, and error not explained well enough -- unless I'm just not sifting through the Apple documentation properly.

P.S. I also saw that a person can create Fetch Requests in the Model Editor; is that useful?
P.P.S. Does the Relationship -> Type: To One/To Many apply to the selected model (one favorite to one recipe) or the model (one recipe to one favorite) it is referring to? -- these kinds of things I can't find answers or useful docs for. I also ask because I've considered having ingredients and instructions as separate models in one-to-many relationships.
P.P.P.S. Favorite is seperate because I was thinking that might be necessary when I finally get to CloudKit. I want the Recipes to be updated via the cloud and Favorites as optionally synced for the user on their iCloud space.

Looks like your SwiftUI issue has been resolved so I'll focus on the P.S. points:
- I've not created fetch requests in the model editor, but have had no problem with the classic approach so I'd recommend that.
- The relationship type refers specifically to the relationship you've selected (sounds obvious I know but the model editor is confusing). So if you select the `recipe` relationship in your `Favorite` model and set it to one-to-many that means that when generated the Favorite class will have a set of recipes. If you then select the `Relationship` model in the model editor you'll typically want a relationship reference to `Favorite`. Once both are created you can then set the inverse relationship (which means that when you set a relationship from Favorite to Recipe then the inverse relationship from Recipe to Favorite is automatically set for you).
- Imo in general I'd keep the `isFavorited` on the `Recipe` and not have the `Favorite`, but I don't know enough about the context here. When it comes to syncing with CloudKit I think you could just omit the `isFavorited` property from being synced.
- As a general model idea you could indeed have a Recipe that has a reference to an Instructions object. That instructions object could then contain a set of InstructionStep objects (which will need to be ordered - that's configurable in the model editor I think). Then for the ingredients I guess you'd need a ServingSize object between Recipe and Ingredient (e.g. 100g beef, 1 tomato). Feel free to ignore this, though - just a recommendation.

Regarding the CoreData class generation if it's working well for you by all means stick with it. I only mention it as in my experience (from a year or so ago) it was problematic. I found this tutorial, which imo is a good starting point because it works with the best practices of CoreData without being overwhelming and also uses SwiftUI.

Don't give up, Apple documentation as of late has been quite poor and SwiftUI does suffer from confusing errors. CoreData is also quite out-of-date in terms of Swift design patterns, so don't fret about it (hopefully they'll update it this year if we're lucky!)

Also, a really good SwiftUI (and Swift in general) resource is Hacking With Swift - the website also features a SwiftUI by example section
 
Last edited:
  • Like
Reactions: MacCheetah3
Very helpful info, thanks, everyone!
Regarding the CoreData class generation if it's working well for you by all means stick with it. I only mention it as in my experience (from a year or so ago) it was problematic. I found this tutorial, which imo is a good starting point because it works with the best practices of CoreData without being overwhelming and also uses SwiftUI.
I like auto-filled code, and I know auto-generated stuff isn't meant to be all-encompassing or one size fits all, but, for example, Apple's devs not thinking a Core Data model class will probably be used in a list, foreach, etc and include Identifiable as default is a little baffling.
Don't give up, Apple documentation as of late has been quite poor and SwiftUI does suffer from confusing errors. CoreData is also quite out-of-date in terms of Swift design patterns, so don't fret about it (hopefully they'll update it this year if we're lucky!)
Thanks for the encouragement :) . I'm willing to do the backend/leg work, even though WYSIWYG creators can be helpful. However, now that I've dug deeper, I'm dreadfully tempted to say, Microsoft is definitely competing well against Apple in the dev department. MS's documentation isn't much better overall, and their IDE auto-fix suggestions are probably on par, but at least in apps such as Visual Studio you can hover over elements and get more info, e.g., parenthesis on a function and see what parameters you're satisfying or at least what parameters your arguments are being associated to (sometimes what parameters need yet to be satisfied).

Again, just more info would go a LONG way.
Also, a really good SwiftUI (and Swift in general) resource is Hacking With Swift - the website also features a SwiftUI by example section
Appears to have quite the repository of useful examples. I will certainly look over it more closely.

P.S. I know my Mac is nearing obsoletion, so Xcode doesn't update, including code suggestions and errors, quickly which probably doesn't help the situation. I do plan on buying a new Mac mini but probably not until later this year -- hopefully, a new generation is released.
 
Last edited:
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.