Swift: Unable To Set Description on Subclass of NSObject - swift

I have built a custom class that looks something like this:
import UIKit
class Device: NSObject {
var id = String()
var name = String()
var type = String()
//var description = String() //Cannot override with a stored property 'description'
}
Very simple class, and I am inheriting NSObject so I can use "setValue(value, forKey: keyName)". However, when I do the following:
device.setValue("My Description", forKey: "description")
I am getting the following error:
'NSUnknownKeyException', reason: '[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key description.'
So insummary, I can't override the NSObject.description, but when I try to set it I am getting an error. Anyone run into this before?

Look at where description is defined. It is listed in the NSObjectProtocol as
public var description: String { get }
You can only get the description property, you can't set it.
In Objective-C, description is implemented in most classes as a method; it has no underlined storage. The swift equivalent would be a computed property:
public override var description: String {
return "I'm an object"
}
tl;dr Use a computed property instead of a stored property
class CustomObject : NSObject {
private var des: String
override var description: String {
get {
return des
}
set(newValue) {
des = newValue
}
}
init(string: String) {
des = string
}
}

You can only get the description property, you can't set it.
In Objective-C string classes, the class description for NSMutableString specifies that the class inherits from NSString. description, when you try to set description it will be getting an error.
Method 1
While using setValue(value, forKey: keyName) Use can store property value by using.
class ObjName: NSObject{
var id: String?
var descriptions : String?
override var description: String {
get {
return self.description
}
set(newvalue) {
descriptions = newvalue
}
}
}
Using above code, method setValue for key description value store into the descriptions. while you get the value you can use descriptions. Also, it does not affect on description get.
Method 2
Overriding setValue function. like below.
class ObjName: NSObject{
var id: String?
var descriptions : String?
override func setValue(_ value: Any?, forKey key: String) {
if key == "description"{
if let desc = value as? String{
self.descriptions = String()
self.descriptions = desc
}
}else{
super.setValue(value, forKey: key)
}
}
}

Related

Method cannot be marked #objc because the type of the parameter 2 cannot be represented in Objective-C [duplicate]

After I have updated Swift 1 to Swift 2.0 I have an issue.
I am getting the following error on the first line of this code:
Method cannot be marked #objc because the type of the parameter cannot be represented in Objective-C
#objc func personsToFirstStep(persons: [Person]) {
for person in persons {
if !self.persons.contains(person) && person.id != userID {
self.persons.append(person)
}
}
collectionView.reloadData()
collectionViewPlaceholder.hidden = true
collectionView.hidden = false
collectionGradientView.hidden = false
}
This this Person class:
class Person: Hashable {
var intID: Int = 0
var id: String = ""
var name: String = ""
var type: String = ""
var hashValue: Int {
return self.intID
}
init(id: String, name: String, type: String) {
self.id = id
self.intID = Int(id)!
self.name = name
self.type = type
}
}
func ==(lhs: Person, rhs: Person) -> Bool {
return lhs.intID == rhs.intID
}
You have very nicely explained the problem yourself:
class Person: Hashable {
Person is not an NSObject. But only an NSObject-derived class type can be seen by Objective-C. Therefore your Person type is invisible to Objective-C. But your #objc func declaration is for a function that takes an array of Person — and we have just said that Person is invisible to Objective-C. So your #objc func declaration is illegal. Objective-C cannot be shown this function, because it cannot be shown its parameter.
You would need to change your class declaration to start like this:
class Person: NSObject {
...and then you might of course have to make any necessary further adjustments in the class's implementation. But that change would make your #objc func declaration legal. (NSObject is Hashable, so the amount of work needed to make this adaptation might not be very great.)
I was getting this because I declared a class Notification of my own and it was messing with Foundation's Notification class.
#objc func playerItemDidReachEnd(notification: Notification) {...}
So I changed it to Foundation.Notification
#objc func playerItemDidReachEnd(notification: Foundation.Notification) {...}
With this less informations I can only try to suggest you to put this before Person declaration.
#objc(Person)
class Person {
...
}

NSPredicate NSUnknownKeyException - Swift 4.0

I have my current code as listed below:
class ViewController:UIViewController{
ovveride func viewDidLoad(){
filterData()
}
func filterData(){
var usersArray = [User(name:"John",userId:1),User(name:"Ed",userId:2),User(name:"Ron",userId:3)]
let filteredData = (userArray as NSArray).filtered(using:NSPredicate(format: "userId=1"))
}
}
The above code throws throws the following Exception (NSUnknownKeyException):
The Object Type '[<ViewController.User 0x6000000afa20> valueForUndefinedKey:]':
this class is not key value coding-compliant for the key userId.
The document of Apple for filter(using:) does not specify any changes that could be causing this issue.
How can I use NSPredicate in Swift 4.0?
Also, as requested, I tried using #objc. But, still the same error.
#objc
class User:NSObject{
var name:String!
var userId:Int!
init(name:String,userId:Int){
self.name = name
self.userId = userId
}
}
With Further Comments received for this question, on adding #objc attribute to the userId I receive the below message.
property cannot be marked #objc because its type cannot be represented in Objective-C
#objc
class User:NSObject{
#objc var name:String!
var userId:Int! //#objc here results in error
init(name:String,userId:Int){
self.name = name
self.userId = userId
}
}
String property NSPredicate it works completely fine.
- Add #objc to class
- Also, add #objc to property
Why not just use filter? It's much more "Swift-y" and doesn't involve converting your array to an NSArray.
let filteredData = usersArray.filter { $0.userId == 1 }

Getting an error when creating a variable that is an array of a custom class

I have a custom class called Message.
import UIKit
class Message {
var sender: String
var message: String
init?(sender: String, message: String) {
self.sender = sender
self.message = message
}
}
I also have a custom class called Chat that has a variable, called messageList, that is an array of the Message class.
class Chat {
//MARK: Properties
var name: String
var image: UIImage?
var animal: String
var messageList = [Message]()
//MARK: Initialisation
init?(name: String, image: UIImage?, animal: String) {
if name.isEmpty || animal.isEmpty {
return nil
}
self.name = name
self.image = image
self.animal = animal
let firstMessage = Message(sender: "animal", message: "Hi! Nice meeting you!")
self.messageList.append(firstMessage)
}
}
I have tried many different ways, but each time, I get an error when messageList is declared saying the following: "Property cannot be marked #NSManaged because its type cannot be represented in Objective-C" or "Property cannot be declared public because its type uses an internal type".
Thank you in advance,
Alex
Classes are declared as internal by default, so you have to add the public keyword to make them public.
import UIKit
public class Message {
var sender: String
var message: String
init?(sender: String, message: String) {
self.sender = sender
self.message = message
}
}

Swift 2.0 Method cannot be marked #objc because the type of the parameter cannot be represented in Objective-C

After I have updated Swift 1 to Swift 2.0 I have an issue.
I am getting the following error on the first line of this code:
Method cannot be marked #objc because the type of the parameter cannot be represented in Objective-C
#objc func personsToFirstStep(persons: [Person]) {
for person in persons {
if !self.persons.contains(person) && person.id != userID {
self.persons.append(person)
}
}
collectionView.reloadData()
collectionViewPlaceholder.hidden = true
collectionView.hidden = false
collectionGradientView.hidden = false
}
This this Person class:
class Person: Hashable {
var intID: Int = 0
var id: String = ""
var name: String = ""
var type: String = ""
var hashValue: Int {
return self.intID
}
init(id: String, name: String, type: String) {
self.id = id
self.intID = Int(id)!
self.name = name
self.type = type
}
}
func ==(lhs: Person, rhs: Person) -> Bool {
return lhs.intID == rhs.intID
}
You have very nicely explained the problem yourself:
class Person: Hashable {
Person is not an NSObject. But only an NSObject-derived class type can be seen by Objective-C. Therefore your Person type is invisible to Objective-C. But your #objc func declaration is for a function that takes an array of Person — and we have just said that Person is invisible to Objective-C. So your #objc func declaration is illegal. Objective-C cannot be shown this function, because it cannot be shown its parameter.
You would need to change your class declaration to start like this:
class Person: NSObject {
...and then you might of course have to make any necessary further adjustments in the class's implementation. But that change would make your #objc func declaration legal. (NSObject is Hashable, so the amount of work needed to make this adaptation might not be very great.)
I was getting this because I declared a class Notification of my own and it was messing with Foundation's Notification class.
#objc func playerItemDidReachEnd(notification: Notification) {...}
So I changed it to Foundation.Notification
#objc func playerItemDidReachEnd(notification: Foundation.Notification) {...}
With this less informations I can only try to suggest you to put this before Person declaration.
#objc(Person)
class Person {
...
}

using setValue(value, forKey) for generic property

I have the following sample code:
class A<T> : NSObject {
var value: T? = nil
}
class B<T> : NSObject {
private var _value: T? = nil
var value: AnyObject?{
get{
return _value as? AnyObject
}
set{
_value = newValue as? T
}
}
}
var obj = B<Int>()
print(obj.value) // "nil"
obj.setValue(5, forKey: "value")
print(obj.value!) // "5"
This works fine if I use B class. However, my target is to use smaller class A which I can't use because of the exception thrown on the setValue function:
this class is not key value coding-compliant for the key value
Is there is a way to pass value to the generic property in the setValue function? And if it is official restriction, where I can read about this?