I've created simple entity 'CDWorkout' with one attribute 'name' inside CDModel.xcdatamodeld. Name of container in AppDelegate is also 'CDModel'. Class Codegen for 'CDWorkout' is Category/Extension. Here is code for CDWorkout class:
class CDWorkout: NSManagedObject {
class func createWorkout(workoutInfo : Workout, in context: NSManagedObjectContext) -> CDWorkout{
let workout = CDWorkout(context: context)
workout.name = "anyName"
return workout
}
}
the createWorkout function is called from another viewController with context argument as container.viewContext but it immediately crashes with message:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'An NSManagedObject of class 'Workout_Generator.CDWorkout' must have a valid NSEntityDescription.'
What did i forget about?
The issue I had was that I needed to have the Class Module set to Current Product Module for my CDWorkout Entity.
In Xcode 10 there is a drop down in the Class section of the Data Model Inspector.
I had a silly minor issue that resulted in the same error. I was initializing NSPersistentContainer with the incorrect name.
It should have the same name as the source file with the extension .xcdatamodeld. e.g. modelFileName.xcdatamodelId
let persistentContainer = NSPersistentContainer(name: "modelFileName")
In my case the same problem was in #objc modifier in auto-generated header of data-class
#objc(CachedMovie)
public class CachedMovie: NSManagedObject {
I just removed #objc(CachedMovie) and it began to work
I came across this same error message when trying to insert/add a managed object from an extension (SiriKit). It seems to have been an issue with the namespace of the extension not matching the .xcdatamodeld file, because I was creating the entity description using MyClass.entity().
The combination that worked for me was:
#objc(MyClass) at the top of each NSManagedObject subclass
Entities in the data model use the default "Global namespace", not "Current Product Module"
Create the entity description using let entity = NSEntityDescription.entity(forEntityName: "MyClass", in: context)!
In AppDelegate check persistent container name.
let container = NSPersistentContainer(name: "CoreData")
If you trying to reach nonexistent Core Data, that may manifest as this error.
Name should be exact same as .xcdatamodeld object in navigation menu.
If you are using coreData from a static library make sure you specify the module aswell
This gives you the correct entity access MyStaticLibrary.MyManagedObject
So if your receiving warnings with this dot notation, its not looking in your module
One way this could happen is by trying to rename your entities. Make sure that these are the same!
I've got the same issue. In my case I used CoreData initialised and managed in framework wich I integrated with main app via SPM.
Solution
First, in the framework provide module name for entity by hand, by following steps:
open .xcdatamodel,
select Entity which you want to edit,
open Data Model Inspector (the 4th panel in right sidebar),
at section Class element Module by default presents Current Product Module -- enter here your Module name
Repeat these steps for each entity.
Second, in the framework override description for each NSManagedObject you use in project, f.ex. you have:
public class Person: NSManagedObject {
}
override there description with String without module name, like:
public class Person: NSManagedObject {
public override var description: String {
return "Person"
}
}
Code presented above will help if you use convenience initialiser for Person (like Person(context: Context)) which uses description to specify Person.entity().
For me, I forgot to add #objc(Reminder) in this below example. I wrote the NSManagedObject class programmatically.
#objc(Reminder)
public class Reminder: NSManagedObject {
}
I encountered this issue using Swift Package Manager to import a Swift Package that had a model file included as a resource. In my case setting the Module of my entity to Current Production Module produced an incorrect value in the model file. When I used the debugger to print the model after loading the persistent container the fully qualified class name was something like Module_Module.Class instead of the expected Module.Class. I had to manually type the module name instead of using Current Production Module to resolve the issue.
in your file modeldataClass
probably name of Class is incorrect cause before you change name something in your name class
The mistake I made was to change the name of the entity and the generated "NSManagedObject Subclass" and not updating the name of the class. How to fix:
open .xcdatamodeld
click on the entity
open the right panel
Go to Data Model Inspector
Change the name of the Class text field
What fixed it for me was that I had an empty space in the name and didn't notice because of a line break. Removing it fixed the problem..
It was:
container = `NSPersistentContainer(name: "ContainerName ")`
Instead of:
container = NSPersistentContainer(name: "ContainerName")
I had similar issue in CoreData stack with NSManagedObjectModel made from Swift code. The issue was in wrong value for NSEntityDescription.managedObjectClassName attribute. The Swift module prefix was missed.
Correct setup:
let entity = NSEntityDescription()
entity.name = PostEntity.entityName // `PostEntity`
entity.managedObjectClassName = PostEntity.entityClassName // `MyFrameworkName.PostEntity`
entity.properties = [....]
Where: entityName and entityClassName defined like this.
extension NSManagedObject {
public static var entityName: String {
let className = NSStringFromClass(self) // As alternative can be used `self.description()` or `String(describing: self)`
let entityName = className.components(separatedBy: ".").last!
return entityName
}
public static var entityClassName: String {
let className = NSStringFromClass(self)
return className
}
}
I was trying out adding CoreData to existing project and I renamed things a lot. I ended up with multiple .xcdatamodeld without knowing it. The solution was removing .xcdatamodeld and generated NSManagerObject and recreating it again.
it easy to answer to this question. without removed
#objc(Workout)
the solution is on documentary Core Data Programming on "Entity Name and Class Name".
here in your Xcode before doing (Editor -> Create NSManagedObject SubClass)
must change your class Name of Entities to add "MO", CoreData can differentiate between class Name and Entitie Name. and the
#objc(Workout)
will not be created, give us this one:
class CDWorkoutMO: NSManagedObject {
class func createWorkout(workoutInfo : Workout, in context: NSManagedObjectContext) -> CDWorkoutMO {
let workout = CDWorkoutMO(context: context)
workout.name = "anyName"
return workout
}
}
like I do on my Xcode
For SwiftUI you need to also update this method from SceneDelegate:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
//This gets the context from AppDelegate and sets is as moc environment variable
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let contentView = ContentView().environment(\.managedObjectContext, context)
//...
}
In my case, I had my Core Data model inside a Swift Package and had to follow this amazing guide and finally this is what did it:
I opened the model and selected the entity and in the right panel I selected the Data Model Inspector and in the Class panel, I manually entered the Module’s name i.e. the Swift Package name.
For me this worked,
Adding
#objc('YourEntityClassName')
above the class declaration in 'YourEntityClassName+CoreDataClass' file.
Note: I am using this in an SPM package.
Related
I am using realityKit and I want to stop and start a models animation depending on differing events like collisions and user interactions. I started by using a wrapper class to handle this logic, but this seemed to be cumbersome, and my app started crashing when I attempted to make more than one GameEntity.
Example:
class GameEntity: ObservableObject {
let model: ModelEntity
var currentMovementController: AnimationPlaybackController? = nil
// lots of other methods and properties ...
}
So now I am attempting to make a subclass of Entity where I will have an attribute for AnimationPlaybackController:
class ARXEntity: Entity {
var animationHandler: AnimationPlaybackController? = nil
// more methods ...
}
My question is: is it possible to use the AnimationPlaybackController on the entity level? If so, what is the right way to do it?
After creating an updated version of data model and only adding a few new attributes to one entity I'm unable to access the new attributes in my swift view controller files.
In Xcode 10 targeting iOS 12 I've changed the selected Core Data model to the updated version. As I understand it light migration is already enable by default. This seems to be reflected in my console messages when I do my fetch request that shows the new attributes. However, I'm unable to access the new attributes in code within my view controller swift files. I tried adding 'true' flags to MigrateStoreAutomatically and InferMappingModuleAutomatically in my Core Data stack class NSPersistantStoreDescription and am still unable to access the new attribute in code.
// ATTEMPT # ENABLING LIGHT MIGRATION IN CORE DATA
lazy var storeDescription: NSPersistentStoreDescription = {
let description = NSPersistentStoreDescription()
description.shouldMigrateStoreAutomatically = true
description.shouldInferMappingModelAutomatically = false
return description
}()
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "123ABC")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
// CODE IN SWIFT VIEW CONTROLLER TRYING TO ACCESS NEW ATTRIBUTE
var selectedTextPhrase: TextPhrase!
self.selectedTextPhrase.imageURL = "WHAT HAPPENED TO MIGRATION?"
self.selectedTextPhrase.favorite = true
Value of type 'TextPhrase?' has no member 'imageURL' is the message I receive.
The "imageURL" is a new optional attribute that I added to an entity and is a String. The "favorite" attribute is a Boolean and a pre-existing attribute from the prior Core Data model version. I am able to access the "favorite" attribute and not the "imageURL." When typing the code "imageURL" doesn't appear an auto-complete option as the original attributes of the entity from the original Core Data model do.
I figured out the issue. The Swift files created in Xcode for each of my entities did not update after I added new attributes to existing entities. I manually edited the files and added declarations for my new attributes and it now works as expected. Hopefully this saves someone else in the same situation some frustration and time.
In my app I am using a NSPersistantContainer with two NSManagedObjectContexts (viewContext & background context). All my read operations are performed on view context, while all my write operations are performed on the background context (shown below).
Creating a new Animal class
class func new(_ eid: String) {
//Create Animal Entity
let container = CoreDataService.persistentContainer
container.performBackgroundTask { (context) in
let mo = Animal(context: context)
mo.eid = eid
mo.lastModified = Date()
mo.purchaseDate = Date()
mo.uuid = UUID()
do {
try context.save()
}
catch let error {
print(error)
}
}
}
The problem I am having is I need to return the newly created NSManagedObject (Animal) back to the manager class, where the Animal.new(eid) was called, to be used to show the object properties.
I have experimented with using a completion handler, but had issues returning the value, as was using a background NSManagedObject in the main thread.
Using possible new function
Animal.new(eid) { (animal) in
if let animal = animal {
}
What is the best approach for returning a newly created NSManagedObject, created in a background context?
What I do is to keep my CoreData managed objects within my CoreDataManager class, not exposing them out to the rest of my framework. So methods that are to create one or more managed objects would accept the data as an unmanaged object, and then create the managed object but not return it (as callers already have the unmanaged object). When fetching from the CoreDataManager, I'd create and populate a unmanaged object(s) and return that.
Now part of why I do this is because I'm using CoreData in a framework, such that I'd never want to hand a managed object to a client app of my framework. But it also solves other problems like the one you described.
(When I write apps, I use Realm, and those objects can go straight to app's UI classes because Realm is so much easier to, erm, manage. :)
I have a simple problem and documentation is not helping me resolve it.
I have created a Grails v3.3.3 demo project - and created a simple domain class called JsonApiBook, with 'name' attribute like this
package ttrestapi
import grails.rest.*
#Resource (uri='/jsonApiBook', formats=['json','xml'])
class JsonApiBook {
static constraints = {
}
String name
}
and marked up the URI as the documentation says the JSON API rendering only works with domain classes (and not a controller class).
In my bootstrap I have saved a instance of book to the tables - and can view that generally.
In my views directory I have a created jsonApiBook folder and created two gson files.
A '_jsonApIBook' template like this
import ttrestapi.JsonApiBook
model {
JsonApiBook book
}
json jsonapi.render(book)
which invokes the jsonapi helper object to render the instance.
I have in the same directory created an index.json like this:
import ttrestapi.Book
model {
List<Book> bookList
}
// We can use template namespace
// method with a Collection.
json tmpl.book(bookList)
When I run the app and use postman or browser to render then I get a result but its Json api compliant (I think it's ignored the template).
So localhost:8080/jsonApiBook just returns (looks default layout):
[
{
"id": 1,
"name": "json api book3"
}
]
and localhost:8080/jsonApiBook/1 just returns 'null' which can't be right.
How should I be setting up the json views for rendering JSON API compliant output? As this doesn't appear to work correctly.
build.gradle
buildscript {
....
dependencies {
........
classpath "org.grails.plugins:views-gradle:1.2.7"
}
}
--
apply plugin: "org.grails.grails-web"
apply plugin: "org.grails.plugins.views-json"
dependencies {
. . .
compile "org.grails.plugins:views-json:1.2.7"
. . .
}
Domain JsonApiBook.groovy
import grails.rest.Resource
#Resource (uri='/jsonApiBook', formats=['json','xml'])
class JsonApiBook {
String name
static constraints = {
}
}
Bootstrap.groovy
class BootStrap {
def init = { servletContext ->
new JsonApiBook(name: 'first').save(flush:true)
new JsonApiBook(name: 'second').save(flush:true)
new JsonApiBook(name: 'third').save(flush:true)
new JsonApiBook(name: 'fourth').save(flush:true)
new JsonApiBook(name: 'fifth').save(flush:true)
}
def destroy = {
}
}
Created folder under view called jsonApiBook
Created template named _jsonApiBook.gson in jsonApiBook folder
model {
JsonApiBook jsonApiBook
}
json {
name jsonApiBook.name
}
created show.gson under same folder
model {
JsonApiBook jsonApiBook
}
json g.render(template:"jsonApiBook", model:[jsonApiBook:jsonApiBook])
When i run http://localhost:8080/jsonApiBook i get bellow:
When i run http://localhost:8080/jsonApiBook/1 i get bellow:
Note: I used grails 3.3.3 with h2 memory DB
Reference
Hope this helps you
ok - got to similar place today on the train. Essentially the convention over configuration is core to whats happening here.
First the #Resource annotation generates a default RestfulController for you. In this approach the default base template _resourceClassName.gson expects the model variable to have the same name as the resource type so my original example instead of 'book'
import ttrestapi.JsonApiBook
model {
JsonApiBook book
}
json jsonapi.render(book)
it should really read as (following convention)
import ttrestapi.JsonApiBook
// variable should be same name as the Class name starting with lowercase
// as default (it can be different but the caller has to change how the
// the template parameter is invoked
model {
JsonApiBook jsonApiBook
}
json jsonapi.render(jsonApiBook)
Then the index.gson should have read as modified below
import ttrestapi.JsonBookApi
//note although not obvious in the written docs which use the show command, the
// default expected model variable is <resourceClass>List
model {
List<JsonBookApi> jsonBookApiList
}
// We can use template namespace
// method with a Collection.
json tmpl.jsonBookApi (jsonBookApiList )
If you want to use another variable name then in the base template you'd have to declare that name as map when calling the base template, from the index.gson . e.g. say the variable name in the base template was
model {
JsonBookApi myBook...
then when calling this template from my index.gson you would put something like this
...
model {
List<JsonBookApi> jsonBookApiList
}
json tmpl.jsonBookApi ("myBook", jsonBookApiList )
this invokes the correct template _jsonBookApi, but takes the model variable default in the index.gson and forces it to bind the value of jsonBookApiList to the myBook variable in the base template (_jsonBookApi.gson).
With the default generation of a controller, using #Resource annotation, the model variable will always be 'resourceClassName'List
I think the only way to change that is not to use the #Resource annotation on your domain class, but to use the URL mappings configuration to map your uri to a controller, and then you have to create a controller yourself by hand and ensure you extend from RestfulController. doing this you can override the default model variable name by implementing an overidden 'index()' method and ensuring you explicitly name the model variable you want, and ensure that the index.gson model variable is exactly the same as that set in your controller.
however the key point was I was not following the core convention defaults so the code as originally built couldn't work and returned null.
when you start out the documentation isn't absolutely clear what bits are part of the convention, and in the examples (which use show.gson) don't tell you what the model variable default name will be for the index.gson (add List to end) so its quite easy to get lost
I am facing the following problem:
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(typeof(Type1).Assembly));
catalog.Catalogs.Add(new AssemblyCatalog(typeof(Type2).Assembly));
using (CompositionContainer container = new CompositionContainer(catalog))
{
}
I need one more export:
Export[(typeof(Type3))]
The thing is that I can't include an assembly with the class which has this Export attribute. I want to tell the container that:
var myObject = new Type4();
myObject (the instance of Type4) should be exported each time the Import[(typeof(Type3))] is needed. Besides I can't mark Type4 with Export[(typeof(Type3))] and also I want the instance of the class to be used by MEF (so marking this class with Export attribute doesn't work, because I am changing myObject before I pass it to MEF and I want it to be used to satisfy Import).
Then when I try to do:
container.SatisfyImportsOnce(importer);
I expect that MEF will get all the objects from the assemblies in catalog, and for the missing Type3 it will use myObject. This should be the value when I do:
container.GetExportedValue<Type3>();
I spent one day trying different approaches: custom ExporterProvider and some sort of inheritance from Type4 to mark it with proper Export attribute but I can't get it working as I want.
I would be very grateful for help.
Thank you!
Ok, already found an answer.
First problem was that I added 2 the same AssemblyCatalogs to AggregateCatalog - don't do that.
The solution is to use CompositionBatch:
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(typeof(Type1).Assembly));
catalog.Catalogs.Add(new AssemblyCatalog(typeof(Type2).Assembly));
var myObject = new Type4();
using (CompositionContainer container = new CompositionContainer(catalog))
{
var batch = new CompositionBatch();
Export ex = CreateExport<Type3>(myObject); //Custom implementation
batch.AddExport(ex);
container.Compose(batch);
var val = container.GetExportedValue<Type3>(); //value == myObject
}
Thank you!