1
votes

The code I’m working on now (inherited from another team) has a persistence layer based on CoreData.

One of the entities is named “Notification”, and it represents messages periodically polled by the client app from the back end (unrelated to APS); the corresponding NSManagedObject subclass is defined the following way:

import Foundation
import CoreData

@objc(Notification) public class Notification: NSManagedObject {

    // etc...

Needless to say, this class name shadows the homonymous Foundation type (counterpart to NSNotification on the Objective-C side). The app does not use the NotificationCenter machinery, so it wasn't a problem until now.

Now I need to introduce notifications for some of my classes to observe certain app-level events, and I don’t want to have to disambiguate each and every time, e.g.

let notification = Foundation.Notification.Name(...

I am aware that I cannot change the class names of my CoreData entities without breaking compatibility, but I thought that the @objc(Notification) would let me change the Swift class name; for example:

@objc(Notification) public class AppNotification: NSManagedObject {
//    ^ This stays the same      ^ This changes

...after all, CoreData is an Objective-C API. It makes sense that, if I have to explicitly specify the Objective-C bridged class name, perhaps I can get away with a Swift class name that is different from the model.

But no, my app crashes if I make the change above.

Is there a way around this, or am I stuck with the horrible decision (and lack of foresight) of the original author?

1
How about using a typealias for Foundation.Notification? - Sweeper
Great idea. Now I have to come up with a decent and meaningful name - Nicolas Miari
it is preferable to avoid name collisions with standard objective-c based frameworks names at all... it is still objective-c in run-time and you might got into very unexpected and unclear run-time troubles. So just make your names unique and you always be sure "who is who". - Asperi
You're sort of lucky, because the @objc(Notification) doesn't interfere with the Foundation.Notification - the reference Obj-C type is NSNotification. Anyway, you can change the class name (see my answer) and it shouldn't crash. If it crashes, there're some other problems in your code base which were hidden till now (like someone was using className to get entity name, ...). Or it can be the code generation issue, old derived data, ... Include the stack trace so we can see what's going on. - zrzka

1 Answers

2
votes

Initial state

Let's say that you have the following class in Swift:

@objc(Notification)
class Notification: NSManagedObject {

}

extension Notification {
    @nonobjc public class func fetchRequest() -> NSFetchRequest<Notification> {
        return NSFetchRequest<Notification>(entityName: "Notification")
    }
}

extension Notification : Identifiable {
}

And the following entity in your model:

enter image description here

Change class name

Can I change class name? Sure. Just update your code:

@objc(MyNotification)
class MyNotification: NSManagedObject {
}

extension MyNotification {
    @nonobjc public class func fetchRequest() -> NSFetchRequest<MyNotification> {
        return NSFetchRequest<MyNotification>(entityName: "Notification")
    }
}

extension MyNotification : Identifiable {
}

And your model:

enter image description here

It's important to keep the Entity Name set to Notification. There's no need for migration if you keep the Entity Name untouched.

Other comments

  • You should include the crash & stack trace in the question if it crashes after change like this one.
  • It's possible that it crashes because of another problem in the code.
  • I saw that people are still using className for entity name, which is utterly wrong, because NSManagedObject subclass class name can differ from the entity name. Perhaps, this is your problem? Don't know.
  • This is one of the reasons we have NSEntityDescription & name.
  • Also you probably noticed that I'm using Manual/None in the Codegen. That's because I wanted to avoid any code generation interference when I was testing the class name change.
  • You can change the Entity Name as well, but then you'll need migration.