I'm getting a "Table name too long" error after moving some code into a framework. After going through the stack trace, breaking to get the table names on schema creation, then manually trying to create the longer table names. I've determined the name that's problematic. The table name that's problematic is a linking table, is there a way in Realm to force the name of that table using className() or something else for a linking table?
Screenshot of the Realm error being thrown.
After further investigation it turns out the way to resolve this was to create a subclass of the type in my app. Due to the how my code was structured, Realm was creating the table name using the class name, package name and the generic type handed in. This made the name way too long. When you subclass the type with it's explicit generic type in the actual app, Realm no longer needs to worry about package names or the generic name. Below should help illustrate the problem and solution.
class PackageA.One<I>: RealmSwift.Object {
var List<I> = List<I>()
}
class App.Two: RealmSwift.Object {
}
let realmObjectsToRegister = [Package.One<App.Two>.self]
The above lead to Realm creating a Table name of "TtGC11PackageA9OneC9App18Two" except the real names in my app made this over 57 characters (the max table name length).
By doing the following, i shortened the name and fixed the problem
class PackageA.One<I>: RealmSwift.Object {
var List<I> = List<I>()
}
class App.Two: RealmSwift.Object {
}
class App.AppOne: PackageA.One<App.Two> {}
let realmObjectsToRegister = [App.AppOne.self]
The solution then lead to realm naming the table "AppOne" instead and fixed the long name issue.
Related
I have an Objective-C model class MyType. This class is used in Swift code:
NSEntityDescription.insertNewObjectForEntityForName("MyType", inManagedObjectContext: context) as! MyType
The as! cast results in the error message
Core Data: Could not cast value of type 'MyType_MyType_2' (0x7be99a30) to MyType (0xf33db74).
If I cast it as NSManagedObject it works. When I print the result, I can nevertheless see, that it is an actual instance of MyType:
<MyType: 0x7ae06d50> (entity: MyType; id: 0x7aeba6d0 <x-coredata:///MyType/t957F2860-85F8-46E0-B6D6-4D1DF6C4EC613> ; data: {
...the fields...
})
What is happening here? And where does the name MyType_MyType_2 come from?
When I had this issue, it was because I had forgotten to set the "class" on the entity. This is what I came up with:
Click on .xcdatamodelId file in your file structure/project
navigator pane (far left).
Select the entity that you are having issues with.
In the Utilities pane (far right), look for the icon that looks like a 1997 cell phone, the Data Model Inspector.
Under the entity settings, you should see two fields, "Name" and "Class" - set up "Class" with the name of the class you are using (typically the same as "Name").
You'll even notice before you follow these steps that the default "class" is NSObject, reflecting the error message. I found some programatic ways to do this too, but this seemed like the simplest/quickest solution.
I should note that my model WAS written in Swift, so I did have to add the #objc(Entity) interoperability reference mentioned by #zellb. But that shouldn't make a difference in the solution as long as you are doing that part properly (and that would cause a different unrelated error from my understanding).
Set Entity Class Name
Set Module "Current Product Module" like below
just try this:
#objc(MyType)
public class MyType: NSManagedObject {
// your class
}
instead of this:
class MyType: NSManagedObject {
// your class
}
I had mistakenly set a "parent entity" in one of my entities in the data model inspector in the entity section. I mistakenly thought that referred to the destination of a one-to-many relationship.
Setting it back to "no parent entity" fixed the problem, although I did have to delete and reinstall the app in the simulator to deal with the messed up core data database.
Running the following code aborts in the return line
Type is not Workout
Could not cast value of type
'NSManagedObject_Workout_' (0x7fcca20620f0) to
'AppName.Workout' (0x100ea5f40)).
The part inside if let... is never executed.
func createWorkoutWithName (name: String) -> Workout? {
let entityName = NSStringFromClass(Workout.classForCoder())
let newEntity = NSEntityDescription.insertNewObjectForEntityForName(entityName, inManagedObjectContext: managedObjectContext)
if let newEntity = newEntity as? Workout {
newEntity.name = name
}
NSLog("createWorkoutWithName: Type is not Workout")
return (newEntity as! Workout)
}
I had this problem in the past and I solved it in XCode 6.x by going in the entity inspector and setting Class = AppName.Workout
One of several answers that suggests this solution is
How come I can cast to NSManagedObject but not to my entity's type?
XCode 7 adds a new twist to this problem:
When I set
Class = AppName.Workout
in the entity inspector, XCode 7 changes the class name automagically to
Class = AppNameWorkout
by removing the dot between AppName and ClassName.
So how can I do this in XCode 7 when I can't set a dot between AppName and ClassName?
Go to your cdatamodel..where you declared your entity..There is a Default Configuration below the Entity. Check the Class column against your entity. By default this will have a dot prefixed. Remove the dot prefix .And it should work. And from what i see is you do not have to prefix your entity class name with the module name in Xcode 7. Hence you cannot use a dot in the class name.I am a newbie in iOS development. So i might not be totally right. But removing the dot prefix solved my issue in Xcode 7.
You need two fixes:
First: Set your entity class name as your entity name. For example:
Entity name = Entity
Class name = Entity
Second: add this code above your entity class file:
#obj(Entity name)
For example:
#obj(Entity)
Class Entity: NSManagedObject {
...
}
The combination of these solutions and the link you provided got me to the solution, but it took me a while to piece it all together. This is what I came up with (and it was basically just an oversight):
Click on .xcdatamodelId file in your file structure/project
navigator pane (far left).
Select the entity that you are having issues with.
In the Utilities pane (far right), look for the icon that looks like a 1997 cell phone, the Data Model Inspector.
Under the entity settings, you should see two fields, "Name" and "Class" - set up "Class" with the name of the class you are using (typically the same as "Name").
Thats where I messed up, even though all my other entities had this set, I had forgotten to set this on this particular entity. You'll even notice before you follow these steps that the default "class" is NSObject, reflecting the error message. Some of the other solutions here likely ultimately do the same thing, but I found this the simplest/quickest solution.
I should note that I already had some of the other items noted set, like the #objc(Entity) interoperability reference.
I'm having trouble working out the implications of this note from Apple's "Using Swift with Cocoa and Objective-C":
Swift classes are namespaced—they’re scoped to the module (typically, the project) they are compiled in. To use a Swift subclass of the NSManagedObject class with your Core Data model, prefix the class name in the Class field in the model entity inspector with the name of your module.
I've done that, using my own application and just the stock master-detail template, so my entity's name is "Event" and its class is "Stock_Master_Detail.Event". When I then choose Create NSManagedObject Subclass from the Editor menu, and ask it to create a Swift subclass, it doesn't name the class right. Xcode creates a file called "Stock_Master_Detail.swift" with that's for a class called Stock_Master_Detail. And if I create multiple entities, all with the module name prefixed, Xcode can't generate more than one subclass since they'll all wind up having the same name.
I'll add that everything works fine in my limited testing if I just omit the module name entirely, counter to Apple's documentation. My question, then, is, what are the implications of not adding the module name to my class?
One way I got around this is to leave the data model file alone, so in this case the class would be "Event". Then in the code where the NSManagedObjectModel is created (AppDelegate if you used Apple's project template) I changed it to:
lazy var managedObjectModel: NSManagedObjectModel = {
// The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model.
let modelURL = NSBundle(forClass: self.classForCoder).URLForResource("Model", withExtension: "momd")!
var bundleModel = NSManagedObjectModel(contentsOfURL: modelURL)!
var objectModel = NSManagedObjectModel()
objectModel.entities = bundleModel.entities.map { ( entity ) -> NSEntityDescription in
var entityCopy = entity.copy() as NSEntityDescription
entityCopy.managedObjectClassName = "Stock_Master_Detail." + entity.managedObjectClassName
return entityCopy
}
return objectModel
}()
One improvement, I would like to figure out is how to find the module name dynamically. I know I could parse the current class's name, but that feels a weirder than I want.
I believe that it could be a bug with Swift/Core Data. First, generate your managed object without the module name(prefix). After it is generated, then go back and add the prefix in core data.
I have an simple object that has a name
public class Foo {
private String name
}
Each user on the site may have up to 10 Foo's associated with them. Within this context, when a new Foo is created, I would like to validate that there isn't another foo associated with the same user that already exists.
I could Create a custom Bean Validator But annotations require the paramaeters to be defined during compilation. How would I then pass across the names of the existing Foos?
As suggested in various places, I could use EL expressions as an alternative way to pick up the data. This feels like using a sledgehammer to crack a nut. It also brings in a whole bunch of potential issues to consider least of all being ease of testing.
I could do class-wide validation using a boolean field
#AssertTrue(message="Name already exists")
public boolean isNameUnique() {
return (existingNames.contains(name));
}
But the validation message would not show up next to the name field. It is a cosmetic issue and this can be a backup plan. However, its not ideal.
Which brings me to the question:
Is there a simple way to write a Bean Validator that can check the value against a collection of values at the field level and meet the following restrictions ?
Previous values determined at runtime
Not using things like EL expressions
Field level validation instead of class level.
EDIT in reponse to Hardy:
The Foo class is an entity persisted within a database. They are picked up and used through a DAO interface.
I could loop through the entities but that means plugging the DAO into the validator and not to mention that the I would need to write the same thing again if I have another class that too has this constraint.
It would help to see how you want to use the Foo class. Can you extend your example code? Are they kept in a list of Foo instances. A custom constraint seems to be a good fit. Why do you need to pass any parameters to the constraints. I would just iterate over the foos and check whether the names are unique.
I'm building a MEF-based plugin-centric WPF application and I'm facing an issue with GetExports, maybe it's just my ignorance but I find an odd behaviour. I have a number of exported parts, all derived from 2 different interfaces (let's name them A and B), but all marked with the same metadata attribute X. So I have code like:
[Export(typeof(A))]
[TheXAttributeHere...]
public class SomePart1 : A { ... }
for each part, and the same for classes implementing B:
[Export(typeof(B))]
[TheXAttributeHere...]
public class SomePart2 : B { ... }
Now, when I try getting all the parts implementing A and decorated by attribute X with some values, MEF returns not only the A-implementing parts, but ALSO the B-implementing parts. So, when I expect to deal with A-objects I get a B, whence a cast exception.
In the real world, interfaces are named IItemPartEditorViewModel and IItemPartEditorView, while their common attribute is named ItemPartEditorAttribute and exposes a PartType string property on which I do some filtering. My code to get parts is thus like e.g.:
var p = (from l in container.GetExports<IItemPartEditorViewModel, IItemPartEditorMetadata>()
where l.Metadata.PartType == sPartType
select l).FirstOrDefault();
When looking for IItemPartEditorViewModel whose PartType is equal to some value, I get the IItemPartEditorView instead of IItemPartEditorViewModel implementing object. If I comment out the attribute in the IItemPartEditorView object instead, I correctly get the IItemPartEditorViewModel implementing object.
Update the suggested "templated" method was used, but I mistyped it here as I forgot to change lessthan and greaterthan into entities. Anyway, reviewing the code I noticed that in the attribute I had "ViewModel" instead or "View" for the interface type, so this was the problem. Shame on me, sorry for bothering :)!
I think I'd need to see more of the code to know for sure what's going on. However, I'd suggest you call GetExports like this:
// Get exports of type A
container.GetExports<A>();
// Get exports of type B
container.GetExports<B>();
Then do your filtering on the list returned. This will probably fix the cast issues you are having. I'd also be interested in seeing the code for the custom metadata attribute. If it derives from ExportAttribute for example, that might be part of the problem.