Define the Data layer - Database

Why only the Animal model not extends CoreDataPersistable? , and the another model like : Contact ,… model can be extends CoreDataPersistable and why that member of Contact init with var properties?

1 Like

This is also something that I noticed and I am not sure why they followed this approach.

I also noticed something when we created the CoreDataPersistable it contains an init() in the requirements of the protocol when we extend the protocol we don’t implement this init() and when we create the extension Breed: CoreDataPersistable { this init() is not required to be implemented by the protocol is there a reason why this is the case because I am trying to create a similar implementation and for my case the protocol CoreDataPersistable requires that the init() should be implemented and I am not sure how to handle it.

Maybe one of the authors can give us more context @renan.dias

Hello!
The reason the Animal model does not extend CoreDataPersistable while other models like Contact do could be due to specific design choices or requirements in your project. The Animal model might be sourced from a different data source that doesn’t require Core Data persistence, or it might have a simpler structure that doesn’t necessitate the use of Core Data. Specific project requirements might also dictate that only certain models need to be persisted using Core Data. As for the Contact model using var properties in its initializer, this allows for mutability, meaning you can modify the properties of a Contact instance after it has been created, which is useful for models that need to be updated frequently.
Best Regards,
PanoramaCharter

Hello hello @minhvuhero9 @kevintopollaj

I believe the reason we didn’t conform Animal to CoreDataPersistable was to demonstrate how that protocol can remove a lot of boilerplate code. Notice that inside the file Animal+CoreData.swift there’s a bunch of code to map AnimalEntity to Animal. While the other models, the ones that conform to CoreDataPersistable, use the default implementation and the Mirror API to store and map values. I believe that was the original idea for not conforming Animal to CoreDataPersistable. But we can also ask @hococoder about it :]

As to why types that conform to CoreDataPersistable are not implementing the required init() from the protocol. In reality, they are, it’s just not explicit. You see, value types (struct) synthesize init for you. So, take the Breed type as an example.

struct Breed: Codable {
  var id: Int?
  var primary: String?
  var secondary: String?
  var mixed: Bool?
  var unknown: Bool?
}

Swift generates 2 init methods for this type. One with with no parameters init() - since all parameters might be nil - and another with all the optional parameters:

init(
    id: Int? = nil,
    primary: String? = nil,
    secondary: String? = nil,
    mixed: Bool? = nil,
    unknown: Bool? = nil
)

So when you conform Breed to CoreDataPersistable, it already has an empty initializer, thus you need only to conform to the other requirements of this protocol (keyMap since all the rest already has a default implementation on the protocol).

You can still write your own init() and initialize the struct some other way (with default values, perhaps), but that’s up to you. If you don’t, it will use the generated init() and initialize with all the properties nill.

Now, if Breed was a reference type (class) you would have to explicitly implement init() as reference types do not synthesize init for you.

1 Like

Thank you for your answer @renan.dias :]

Yes, you are right I totally missed the fact that all the properties in Breed are optional so the struct will create the init() implicitly for us in this case.

Another question that I have now is why we need an init() to set up the object’s basic state when CoreDataPersistable is responsible for doing the transformation from NSManagedObject to API Struct model and the other way around using init(managedObject: ManagedType?) and mutating func toManagedObject(context: NSManagedObjectContext) -> ManagedType?

And if you would have to implement Animal using CoreDataPersistable where some properties of Animal are not optional and we have other objects and enums as types for some properties in the Animal struct how would you do that?

1 Like

I think the general idea of the empty init is that it allows you to create the object and then assign the values you want over time. But I can see how that could be a problem, say, you don’t want a certain object to be created if you don’t pass a specific property. In the end, it’s up to you how you want to initialize this object.

Regarding non-optional properties and Core Data. Core Data is not much of a fan of non-optional types. The reason for that is that Core Data was build with Obj-C back in the day, and some stuff has been inherited into Swift because of that. Meaning, when you model you entities, even if you set that a property must not be optional, Core Data will generate that entity with an optional property. Now, there are ways to work around that, for example, you could write the classes yourself. But I feel like that’s a bit “working against the framework” in the sense that you’ll be doing more work to compensate for something that the framework does not (by design). Usually, what I do with Core Data is, I embrace this. Yes, I have properties that must not be optional and may never have nil or empty values, but I write code to account for that, instead of “extending” Core Data to handle that.

You could, for example, assign default values to those properties, so that no matter how you initialize the object those properties will have some value. Or even create convenience initializers that forces the caller to assign those required properties.

This is a bit different from SwiftData, which is already built on top of Swift so it uses Swift features. In SwiftData, you can declare a non-optional property and the object can only be instantiated with that property.

1 Like