Snapshot.value crashed on setValueForKeys - swift

func observeMessages(){
let ref = Database.database().reference().child("messages")
ref.observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject]{
let message = Message()
print(dictionary)
message.setValuesForKeys(dictionary)
self.messages.append(message)
DispatchQueue.main.async { self.tableView.reloadData() }
}
}, withCancel: nil)
}
This is part of my Firebase&swift project. I keep crashing every time I try to call the message.setValuesForKeys(dictionary).
The error message from the console is
Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key toId
I checked the dictionary and it has the data as I want. I don't know what else I can check. I tried to change the "snapshot.value" to "snapshot.children.allobjects" but with that change I cannot access to the data inside my dictionary.

My first guess is one of your firebase nodes has a key that doesn't exist in your class
Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key toId
^^^^
This is a good clue.
Check through the nodes in your firebase and ensure they all have a key toId and that your class also has a toId property.
Mismatching keys will cause this issue.
Also, if you want to use setObjectForValue, the Message object should inherit from NSObject (which is key-value coding compliant)
Objects typically adopt key-value coding when they inherit from
NSObject (directly or indirectly), which both adopts the
NSKeyValueCoding protocol and provides a default implementation for
the essential methods
If you've checked to ensure your keys all match up then it could be how the Message object is defined. It should look something like this, ensuring it's an NSObject and the keys (properties) start with #objc
class Message: NSObject {
#objc var name = ""
#objc var toId = ""
}
My last suggestion is make your code Swifty and don't rely on NSObject.
class Message {
var name = ""
var toId = ""
func initMessageWithSnap(aSnapshot: Snapshot) {
//desconstuct the snapshot and assign the vars
}
}

Related

Run method on class knowing only its name

how can I launch method knowing its name (as String)
// key.rawValue.firstUppercased is `ApiAddress`
let result = preferencesRepository.perform(Selector("get\(key.rawValue.firstUppercased)"))
where preferencesRepository has method getApiAddress() and conforms to NSObject
public class RealPreferencesRepository: NSObject {
func getApiAddress() -> String
// ...
I have fatal error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '-[PreferencesModule.RealPreferencesRepository getApiAddress]:
unrecognized selector sent to instance 0x600000090a70'
thanks for help in advance
You need to prefix the getApiAddress() method with #objc attribute.
Also, since the return value of the perform method is Unmanaged, you need to use takeRetainedValue() to convert the return value.
public class RealPreferencesRepository: NSObject {
#objc func getApiAddress() -> String {
return "success"
}
}
let preferencesRepository = RealPreferencesRepository()
let result = preferencesRepository.perform(Selector("getApiAddress"))
let value = result?.takeRetainedValue() as! String
print(value)
// => success

Storing a generic subclass in Realm

I have been trying to abstract a little bit my Realm models, so I wanted to create a superclass model with a generic type. Then, create subclasses for the specific type.
I know Realm does not support storing a generic type. However, my question is:
Is it possible to store a specific defined subclass?
So the idea is to have a superclass:
class SuperClass<T: RealmCollectionValue> : Object {
#objc dynamic var pk: String = ""
let listProp = List<T>()
convenience init(pk: String){
self.init()
self.pk = pk
}
override static func primaryKey() -> String? {
return "pk"
}
}
Then implement a specific subclass:
class StringSubClass : SuperClass<String> {}
Is then the possibility to save in Realm instances of StringSubClass (as in fact, it is not a generic)?
From what I have found in this already answered question (which stands it is possible with an accepted answer): Store concrete generic subclass in Realm
You can specify the Realm to ignore the SuperClass by defining it to only handle the StringSubClass.
However, when trying it in Realm 3.13.1 with:
let realm = try! Realm(configuration: Realm.Configuration(objectTypes: [StringSubClass.self]))
try! realm.write {
realm.add(StringSubClass(pk: "mypk"))
}
The following exception is thrown when storing instances of StringSubClass
Terminating app due to uncaught exception 'RLMException', reason: 'Object type 'StringSubClass' is not managed by the Realm. If using a custom `objectClasses` / `objectTypes` array in your configuration, add `StringSubClass` to the list of `objectClasses` / `objectTypes`.'

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.

RLMException: Object type does not match RLMArray type

I have a simple object:
class MyObject : Object {
dynamic var dummyField: String!;
}
and another object which inherits from MyObject:
class MyOtherObject : MyObject {
dynamic var anotherDummyField: String!;
}
Now I do the following. I have a Realm List of MyObject and I create an instance of MyOtherObject and try to save it inside the list:
class Operator {
internal var myObjects: List<MyObject>!;
internal var myObject: MyObject!;
func operate() {
self.myObject = MyOtherObject();
self.myObject.dummyField = "dummy field";
self.myObject.anotherDummyField = "another dummy field";
self.myObjects = List<MyObject>();
self.myObjects.append(myObject); // crash!
}
}
It crashes with the error:
Terminating app due to uncaught exception 'RLMException', reason: 'Object type 'MyOtherObject' does not match RLMArray type 'MyObject'.'
Since MyOtherObject is a subclass of MyObject I cannot understand why the app is crashing at this point with this error message.
Realm Lists are not covarient. You can only store objects of exactly the declared type in them.
self.myObjects = List<MyObject>();
This is not a proper value
self.myObjects = List<Needs actual object here not class>();

JavaScriptCore and Swift "this class is not key value coding-compliant for the key test"

I can't for the life of me pass a dictionary (in this case containing a SKLabelNode) or any other object to JavaScriptCore in Swift. I've taken this short sample straight out of a "command line tool" template project I just created in Xcode.
import Foundation
import JavaScriptCore
import SpriteKit
let context = JSContext()
let label = SKLabelNode(text:"Test")
context.setValuesForKeysWithDictionary(["test": label])
This throws me an exception:
2014-11-16 18:07:47.104 JSCoreTest[10139:193484] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<JSContext 0x1008268d0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key test.'
I am using Xcode 6.1 on Yosemite. Can anyone reproduce my issue and solve it?
UPDATE:
I thought that perhaps it didn't work because I didn't "JSExport" the object, so I tried with this example, but I still get the same error:
protocol MyObjExport: JSExport {
var foo: String! { get }
}
class MyObj: NSObject, MyObjExport {
var foo: String! { return "bar" }
}
let context = JSContext(virtualMachine: JSVirtualMachine())
context.setValuesForKeysWithDictionary(["test": MyObj()])
You're using the wrong method. You want to use setObject:forKeyedSubscript: from JSContext and not setValuesForKeysWithDictionary: from the NSKeyValueCoding protocol (which JSContext does not conform to) i.e. :
let context = JSContext()
let label = SKLabelNode(text:"Test")
context.setObject(label, forKeyedSubscript: "test")