How to handle non-optional Core Data properties in Swift
Explore the possibilities to use non-optional Swift values in Core Data.
07 Nov 2022 · 4 min read
As described in this article on how to create an NSManagedObject subsclass for a Core Data entity in Xcode, we can either let Xcode automatically generate entity subclasses and their properties for us or create and manage them manually.
While the auto code generation has the advantage that the entity classes are always up-to-date, the manual option gives us more control and flexibility for example to improve the handling of non-optional properties.
Let's look at how to do that.

Declaring a property as optional or non-optional in Core Data
When adding properties in Core Data's .xcdatamodeld file, we can decide whether the property should be optional or non-optional:

But after declaring a property as non-optional, Core Data still generates the username property as a Swift optional property:
@NSManaged public var username: String?
That's because the concept of optional and non-optional values in Core Data is different from that in Swift.
The concept behind optional and non-optional values in Core Data
In Swift, the compiler enforces the presence or absence of a value, which gives us guarantees about whether or not it is safe to expect a value at compile time.
Just like in Swift, declaring a property as non-optional in Core Data means that we need to handle the abscence accordingly, but we don't get any compiler guarantees or enforcements when initializing the object. That's because when working with Core Data, the framework checks the values at run time with validation rules and throws an error when the validation fails.
Using non-optional Swift values in Core Data
Not having compiler guarantees for non-optionals when working with Core Data is a drawback. Let's look at how we can solve this.
1. Declaring the properties as non-optional in Swift
To define Core Data properties as Swift non-optionals, we need to manually generate the entity subclasses. After doing that, the first thing we can do is to simply declare the properties as non-optional by removing question marks where needed:
public class User: NSManagedObject {@NSManaged public var username: String@NSManaged public var lastLogin: Date@NSManaged public var verified: Bool}
With that in place, the compiler will prevent us from setting any of these properties to nil. That's a good start.
But since the properties are marked with the @NSManaged attribute - which indicates that Core Data dynamically provides its implementation at runtime - Swift’s initialization rules do not apply. This means that we can still call init() and init(context:) without initializing any of the properties.
2. Preventing initializers from being called
To prevent initializers from being called, we can use Swift's @available attribute to mark the init methods as unavailable.
@available(*, unavailable)public init() {fatalError()}@available(*, unavailable)public init(context: NSManagedObjectContext) {fatalError()}
With that in place, the compiler will now produce an error when calling init() or init(context:).
3. Providing a designated initializer
Now we can provide our own initializer:
public init(context: NSManagedObjectContext,username: String,lastLogin: Date? = nil,verified: Bool = false) {let entity = NSEntityDescription.entity(forEntityName: "User", in: context)!super.init(entity: entity, insertInto: context)self.username = usernameself.lastLogin = lastLoginself.verified = verified}
The initializer enforces non-optional values to be passed in with a value and allows us to define default values where needed.
Conclusion
The approach described above provides a lot more clarity and safety when working with entities in code.
A drawback is that there is a lot of manual work to do to enforce the rules Swift usually enforces for us. Every time we edit an entity, we need to manually keep the initializer and properties up-to-date.

Newsletter
Like to support my work?
Say hi
Related tags
Articles with related topics
Latest articles and tips