Strange issue with setter - swift

I have this code and whenever i am running app it gives me EXC_BAD_ACCESS.
I printed values for self.state and newValue.rawValue by po in terminal of XCode and they have values but i dont understand why i am getting "EXC_BAD_ACCESS" error and "CoreData: warning: Unable to load class named for entity Class not found, using default NSManagedObject instead". I am getting this error when I do following
d.change = .n //d is just instance of class from where state comes
What can be a reason?
var change: A {
get { return (A(rawValue: self.state) ?? .none)! }
set { self.state = newValue.rawValue }
}
enum A: Int16 {
case a = 1
case b = 2
case c = 3
case n = 90
}
state is a property of class which inherits from NSManagedObject
#NSManaged var state: Int16

The Unable to load class named for entity Class error means that you have created the entity in the Core Data model editor but that you didn't tell it what class name to use. Core Data doesn't know that the class Class is used with the entity Class, because the names don't have to be the same so Core Data doesn't assume they are. So it uses a plain old NSManagedObject, but warns you about it.
You can't use properties of Class because you don't have one, so your app crashes.
You fix this by going to the Core Data model editor and making sure that the class name for the entity is correct.

Related

Core Data & Xcode 11: Please switch to using "NSSecureUnarchiveFromData" or a subclass of NSSecureUnarchiveFromDataTransformer

Just moved to Xcode 11 and getting the following crash at launch:
CoreData: fault: One or more models in this application are using transformable properties with transformer names that are either unset, or set to NSKeyedUnarchiveFromDataTransformerName. Please switch to using "NSSecureUnarchiveFromData" or a subclass of NSSecureUnarchiveFromDataTransformer instead. At some point, Core Data will default to using "NSSecureUnarchiveFromData" when nil is specified, and transformable properties containing classes that do not support NSSecureCoding will become unreadable.
CoreData: warning: Property 'color' on Entity 'Group' is using nil or an insecure NSValueTransformer. Please switch to using "NSSecureUnarchiveFromData" or a subclass of NSSecureUnarchiveFromDataTransformer instead.
I'm creating an NSPersistentContainer at launch using the code below:
private let container: NSPersistentContainer = {
let container = NSPersistentContainer(name: "MyApp", managedObjectModel: MyAppModelVersion.current.managedObjectModel())
let storeDescription = NSPersistentStoreDescription(url: getStoreURLWithUserName())
storeDescription.shouldMigrateStoreAutomatically = true
storeDescription.shouldInferMappingModelAutomatically = true
container.persistentStoreDescriptions = [storeDescription]
return container
}()
Error occurs right after this line is executed:
let container = NSPersistentContainer(name: "MyApp", managedObjectModel: MyAppModelVersion.current.managedObjectModel())
I also have a property called 'Colorin aGroup` entity that's transformable:
#NSManaged public var color: UIColor?
#NSManaged public var hexColorValue: String?
Below is how set the property:
public var hexColor: String? {
get {
return self.hexColorValue
}
set {
self.hexColorValue = newValue
if let str = newValue {
self.color = UIColor(hex: str)
}
}
}
This is what the property looks like in Core Data:
I am not sure how to recover from this crash. This was working fine with Xcode 10
Setting Transformer property to NSSecureUnarchiveFromDataTransformer solved the warning in my case. For this select the attribute & set its transformer type to NSSecureUnarchiveFromDataTransformer & run again by pressing commond+R.
Thanks,
Ratneshwar
Swift 5.4.2
This worked for me.
EDIT Link to the article is here.
Click on the .xcdatamodeld file in the project navigator
Click on the
Entity that has a Transformable Attribute
Click on the Transformable Attribute
Click the 'Show Data Model Inspector' icon
Enter 'NSSecureUnarchiveFromDataTransformer'
in the Transformer field - EDIT Make this 'NSSecureUnarchiveFromData' as of Swift 5.5.2.
Your warnings/errors should go away. If not, try cleaning your build folder and rebuild.
This is related to a migration from NSCoding to the NSSecureCoding protocol. The default ValueTransformer adopts NSCoding, so the only solution that worked for me was to write my own Transformer that adopts the NSSecureUnarchiveFromDataTransformer protocol.
I should say that my own experience is from trying to define an Attribute with the Transformer type to persist a custom class that adopted NSCoding. I was initially met with a warning similar to the OP's error. I was able to suppress the warning by changing the Transformer field on the attribute to "NSSecureUnarchiveFromData" as others have mentioned, but I then received an error along the lines of:
Not able to save to CoreData. SQLCore dispatchRequest Object of class “ ” not among allowed top level class list... as mentioned here. The suggestion to change the Attribute to a Relationship was undesirable in my case.
More digging came up with this blog post that details the "reason" for all of this, and gives a solution that worked for me. The blog actually uses the case of UIColor in the example, but it works for any custom class as well.
Say you have a CustomClass that you want to store as a Transformable Attribute in some Entity. If you're like me, you may have adopted NSCoding and received the aforementioned error. The solution would be to adopt NSSecureCoding instead and define an NSSecureUnarchiveFromDataTransformer subclass:
#objc(CustomClassValueTransformer)
final class CustomClassValueTransformer: NSSecureUnarchiveFromDataTransformer {
static let name = NSValueTransformerName(rawValue: String(describing: CustomClass.self))
// Make sure `CustomClass` is in the allowed class list,
// AND any other classes that are encoded in `CustomClass`
override static var allowedTopLevelClasses: [AnyClass] {
// for example... yours may look different
return [CustomClass.self, OtherClass.self, NSArray.self, NSValue.self]
}
/// Registers the transformer.
public static func register() {
let transformer = CustomClassValueTransformer()
ValueTransformer.setValueTransformer(transformer, forName: name)
}
}
Then make sure to set the Transformer field on your Attribute to "CustomClassValueTransformer" and the Custom Class field to "CustomClass" and you should be good to go.
For Objective-C and iOS 14, the following solution works for UIColor attributes.
First add a new subclass of NSSecureUnarchiveFromDataTransformer
#interface ColorValueTransformer : NSSecureUnarchiveFromDataTransformer
Add the following static method to your implementation file:
#implementation ColorValueTransformer
+ (NSArray<Class> *)allowedTopLevelClasses {
return #[UIColor.class];
}
#end
Open your data model (e.g. datamodel..xcdatamodeld)
Select the entity and the related attribute which needs the new Transformer
Open the Data Model Inspector
Add the class name (e.g. ColorValueTransformer) as Transformer to that attribute
Change the Custom Class to UIColor
Build and run…
For the transformable attribute, you need to set its type in the Custom Class field.
For instance, I have a transformable field which stores an array of numbers and its Custom Class is declared as [Int16]. This is most likely the cause of the crash. And as #vadian mentioned before, you don't need both fields.
After your crash is fixed, you can get rid of the warning by setting the Transformer field to NSSecureUnarchiveFromData (you simply type this into the field)
I received the same warning messages when updating to Xcode 11, however in my case they are just warnings, but no crash.
In order to work out the best solution, I tried creating a stripped down sample app with just a single entity containing a transformable attribute. But it seems that no matter what I tried I could not reproduce the problem. The I copied the model file from my main app to the demo app, and of course that failed.
So I got to the point where I just had two model files and a simple unit test which does nothing more than open the model and create a persistent store container:
func testDataModels() {
openDataModel(named: "samplemodel")
openDataModel(named: "appmodel")
}
func openDataModel(named name: String) {
print("Opening \(name)")
guard let url = findFile(forResource: name, withExtension: "momd"),
let managedObjectModel = NSManagedObjectModel(contentsOf: url)
else {
XCTFail("Unable to find \(name) data model")
return
}
print(url)
_ = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)
}
func findFile(forResource name: String, withExtension ext: String) -> URL? {
if let url = Bundle(for: type(of: self)).url(forResource: name, withExtension: ext) {
return url
}
return Bundle.main.url(forResource: name, withExtension: ext)
}
The appmodel causes the error messsages but the sample model does not. Even when I stripped the appmodel down to a single Entity it continues to generate the errors.
Comparing the contents of the samplemodel with the appmodel (show package contents in Finder), there is a hidden file called .xccurrentversion in the samplemodel but not in the appmodel. The file looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>_XCCurrentVersionName</key>
<string>samplemodel.xcdatamodel</string>
</dict>
</plist>
So I created a similar file for the appmodel and put it in the package folder.
Surprisingly, that silences the warning messages! Conversely, deleting the .xccurrentversion file from the samplemodel causes the error messages to be generated. This will allow testing of the problem in isolation.
So this may be a short term fix. In the meantime, I need to work out how to migrate to secure coding.
Maybe some of these answers are acceptable enough, in my case I went to this post. I hope it will be of much help.

Can you run KVC functions in Xcode Playgrounds?

I'm following a tutorial on KVC and KVO when I attempted to enter the code into a playground however it wouldn't run. I received the error "terminating with uncaught exception of type NSException". I even tried to create a single app application and entered the information into a viewController to see what happens and it still wouldn't build which provided the error that the object wasn't key coding compliant. I'd really like to see this work, what am I doing incorrectly?
import UIKit
import Foundation
//this is a reference object which means when it is copied, it will copy a reference to the same instance and not a brand new value like a value type does
class Student: NSObject {
var name: String = ""
var gradeLevel: Int = 0
}
let seat1 = Student()
seat1.setValue("Kelly", forKey: "name")
Your issue is not the playground. Your issue is that to use the Objective-C KVC mechanism, you need to mark the property with #objc.
class Student: NSObject {
#objc var name: String = ""
var gradeLevel: Int = 0
}
Adding that will fix the crash.

EXC_BAD_ACCESS error while running app

I have this code and whenever i am running app it gives me EXC_BAD_ACCESS.
I printed values for self.state and newValue.rawValue by po in terminal of XCode and they have values but i dont understand why i am getting "EXC_BAD_ACCESS" error and "CoreData: warning: Unable to load class named for entity Class not found, using default NSManagedObject instead". I am getting this error when I do following
d.change = .n //d is just instance of class from where state comes
What can be a reason?
var change: A {
get { return A(rawValue: self.state) ?? .n }
set { self.state = newValue.rawValue }
}
enum A: Int16 {
case a = 1
case b = 2
case c = 3
case n = 90
}
state is a property of class which inherits from NSManagedObject
#NSManaged var state: Int16

How should we initialize stored properties in Structs and Classes?

Apple's documentation states:
Classes and structures must set all of their stored properties to an
appropriate initial value by the time an instance of that class or
structure is created. Stored properties cannot be left in an
indeterminate state.
I have also seen this question, but still wasn't able to find an answer.
// struct
struct FlickrObj { // No Error ????
var title : String
var photographer : String
}
// class
class FlickrObj { // will create error: Class 'FlickrObj' has no initializers
var title : String
var photographer : String
}
Apple says both classes and structs must set their stored properties but why doesn't the Struct give any compile time errors?‌
It's because, in the absence of any explicitly declared initializers, the struct itself declares the implicit memberwise initializer init(title:photographer:).
But classes don't do that.
So the class is failing to fulfill the initialization contract (all instance properties must be initialized before the instance itself finishes initializing), but the struct is not failing the contract.

Using the Parse.com Object Column

I cannot find documentation on the Parse.com table column Object data type. I assume it is an object in the context of software development. However, what is the syntax to use to enter an object into the object column? I would be interested to know both the programmatic steps to take (not too concerned about which language, more concerned about actions to take to save to the object column), but I would be even more interested to know how to enter an object into the table from the Parse.com website. Can we do this from the Data section of the "Core" tab, in the Parse.com Dev webpage for the app?
I did this little test in Swift to try and save an object with a property of type object (myCar) to the table from code (I have a Parse.com table with class name Test, which has an object column called myCar). It is causing an error (I'm new to iOS so cannot find out much about the error):
Car class
class Car {
var doors = 4
func addDoor() {
doors++
}
}
client code:
override func viewDidLoad() {
var testMan = PFObject(className:"Test")
var car = Car()
testMan["myCar"] = car //////////////// error here
testMan.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if (success) {
// The object has been saved.
println("Success")
} else {
// There was a problem, check error.description
println("Failure")
}
}
}
well, u're mixing swift objects with parse objects .. if you want a Car object linked to your Test class you must create it in parse database (+ add class -> car -> and then create properties in it). When you have created parse class Car u can use it in swift code as let car = PFObjecT(className: "Car") and later on assign that class to test object with testMan["myCar"] = car