why need to use NSObject? - ios

i am confused about that should i need to used NSObject to swift3 . if so please guide me also is it best practice to used NSNumber
class App: NSObject {
var id: NSNumber?
var name: String?
var category: String?
var imageName: String?
var price: NSNumber?
var screenshots: [String]?
var desc: String?
var appInformation: AnyObject?
// override func setValue(_ value: Any?, forKey key: String) {
// if key == "description" {
// self.desc = value as? String
// } else {
// super.setValue(value, forKey: key)
// }
// }
}
Note : could you tell me please why need to use NSObject ? what is the advantage ?

NSObject class:
This class is the root class of most Objective-C class hierarchies, from which subclasses inherit a basic interface to the runtime system and the ability to behave as Objective-C objects. (source).
AnyObject protocol: its a implicit confirmation of any object.
You use AnyObject when you need the flexibility of an untyped object or when you use bridged Objective-C methods and properties that return an untyped result. AnyObject can be used as the concrete type for an instance of any class, class type, or class-only protocol.
AnyObject can also be used as the concrete type for an instance of a type that bridges to an Objective-C class.
let s: AnyObject = "This is a bridged string." as NSString
print(s is NSString)
// Prints "true"
let v: AnyObject = 100 as NSNumber
print(type(of: v))
// Prints "__NSCFNumber"
The flexible behavior of the AnyObject protocol is similar to Objective-C’s id type. For this reason, imported Objective-C types frequently use AnyObject as the type for properties, method parameters, and return values. (source)
So you can use NSNumber variable as AnyObject which can be later type cast implicitly accordingly.

You're overriding a super class method:
override func setValue(_ value: Any?, forKey key: String) { }
So, if your class doesn't extends NSObject class, how could overrides its super class method?
You could not extend NSObject class, but you'll have to remove override keyword from code since setValue would become a method of your custom class. If you do it, you'll not be able to call super.setValue(value, forKey: key) obviously.

Related

Generic class initialization from protocol

I want to have classes objects JSONSerialization. So my input is [String: Any] and from a documentation I know it may be either NSNull, NSString or NSNumber. So I've made a protocol:
protocol PlainValue { }
and all of those above conform to this protocol:
extension NSString: PlainValue { }
extension NSNull: PlainValue { }
extension NSNumber: PlainValue { }
Then I want to create a class storage that hold key-value like:
class KeyValue<T: PlainValue> {
let key: NSString
let value: T
init(key: NSString, value: T) {
self.key = key
self.value = value
}
}
And want to use it like this:
func parse(json: [String: Any]) {
...
if let value = json[key] as? PlainValue { // this should be `Any` but I want to check here if thats an PlainValue or embedded object
let obj = KeyValue<PlainValue>(key: key, value: value) // this currently not working
...
}
...
}
But my issue is how to declare this object creation on protocol level. If I do this like:
protocol PlainValue {
func convert(key: NSString) -> KeyValue<PlainValue>
}
I'm getting error:
Value of protocol type 'PlainValue' cannot conform to 'PlainValue'; only struct/enum/class types can conform to protocols
Make sense, since I'm already in protocol declaration. So I have a feeling that maybe this should be declared on KeyValue<T> level? But I'm not sure if I'm on a right path for this, since I'm getting compilers error on every approach I'm trying to make. Can anyone point me into right direction how to make it working?
Approach that I feel like it's closest to working is:
extension NSNumber: PlainValue {
func convert(key: NSString) -> KeyValue<NSNumber> {
return KeyValue<NSNumber>(key: key, value: self)
}
}
And similar for above on NSString and NSNull, but not sure how to declare this on protocol level to make this callable from my parsing function. Since
protocol PlainValue: JSONValue {
func convert(key: NSString) -> KeyValue<Self> // this claims that implementation does not match this declaration
func convert<T: PlainValue>(key: NSString) -> KeyValue<T> // same as above
associatedtype Object: PlainValue
func convert(key: NSString) -> KeyValue<Object> // this is working! But... then I cannot check if value is PlainValue in my parsing function, because of `Protocol 'PlainValue' can only be used as a generic constraint because it has Self or associated type requirements`
}
A protocol cannot conform to another protocol, the generic type T in KeyValue<T> must be a concrete type.
An alternative is a protocol extension with an associated type
extension NSString: PlainValue { }
extension NSNull: PlainValue { }
extension NSNumber: PlainValue { }
protocol PlainValue {
associatedtype ValueType : PlainValue = Self
func convert(key: NSString) -> KeyValue<ValueType>
}
extension PlainValue where ValueType == Self {
func convert(key: NSString) -> KeyValue<ValueType> {
return KeyValue<ValueType>(key: key, value: self)
}
}
class KeyValue<T: PlainValue> {
let key: NSString
let value: T
init(key: NSString, value: T) {
self.key = key
self.value = value
}
}
let stringResult = "Foo".convert(key:"bar")
let numberResult = NSNumber(value:12).convert(key:"baz")
let nullResult = NSNull().convert(key:"buz")
Not sure if I understand correctly what you would like to achieve, but based on what you wrote, you don't necessarily need generics here. The code below would resolve your issue, if there are no other factors that make generics necessary in your situation. Also, this way you won't need to declare the object creation on the protocol level.
class KeyValue {
let key: NSString
let value: PlainValue
init(key: NSString, value: PlainValue) {
self.key = key
self.value = value
}
}
Your original example did not work because you needed a concrete type there that conforms to PlainValue. In swift, only concrete types such as struct/enum/class can conform to protocols. For example let obj = KeyValue<NSString>(key: key, value: value) would work there, while using PlainValue would not.

How to use custom initializer on an Realm object inside a class with generic type?

I have a class called ObjectManager which has generic type O: Object, Persistable. This way I can have one class that can operate with multiple different subclasses of realm Object. But the problem is in the last line cache = O(message: "hello world") the error is: Incorrect argument label in call (have 'message:', expected 'value:')
When typing it suggests me that I can use initializer of Persistable protocol, but complain during the compiling, that it expect an initializer from Object class. Is it possible to cast Persistable initializer?
protocol Persistable {
init(message: String)
}
class CustomObject: Object, Persistable {
dynamic var message: String = ""
required convenience init(message: String) {
self.init()
self.message = message
}
}
class ObjectManager<O>: NSObject where O: Object, O: Persistable {
var cache: O?
func didReceive(message: String) {
cache = O(message: "hello world")
}
}
If you make your CustomObject class final, and replace your initializer with a factory method, you will get code which does what you want. Unfortunately, the Swift compiler error messages are so cryptic in this case as to be completely unhelpful.
protocol Persistable {
static func factory(message: String) -> Self
}
final class CustomObject: Object, Persistable {
dynamic var message: String = ""
static func factory(message: String) -> CustomObject {
let x = CustomObject()
x.message = message
return x
}
}
class ObjectManager<O>: NSObject where O: Object, O: Persistable {
var cache: O?
func didReceive(message: String) {
cache = O.factory(message: "hello world")
}
}

Subclass Crashlytics

I seem to be unable to subclass Crashlytics with Swift. My Mock object looks like this:
class MockCrashlytics: Crashlytics {
var newValue: AnyObject?
var newKey: String?
override func setObjectValue(value: AnyObject?, forKey key: String) {
newValue = value
newKey = key
}
}
However, if I instantiate this class, I get the superclass.
Debugger example:
po MockCrashlytics()
<Crashlytics: 0x7fff4188cb30>
I would expect:
po MockCrashlytics()
<MockCrashlytics: 0x7fff4188cb30>
When I try to access newValue or newKey I get EXC_BAD_ACCESS.
Any ideas?
Apparently Crashlytics is not supposed to be subclassed. It seems that some mechanism is preventing proper subclassing, so developers don't tamper with their code.
This information was given by the helpdesk of Crashlytics/Fabric.io.
You could create a wrapper class that holds a field variable of type Crashlytics. Add your variables of newValue and newKey to the wrapper class.
class crashWrapper {
var newValue: AnyObject?
var newKey: String?
//or whatever the correct way to init Crashlytics is
var crashlytics = Crashlytics()
override func setObjectValue(value: AnyObject?, forKey key: String) {
newValue = value
newKey = key
}
}

XCTAssertEqual for custom objects in Swift

XCode 6, Beta 5
I have a unit test like this:
func testMyObjectsEqual()
{
//....
XCTAssertEqual(myObject, myOtherObject, "\(myObject) and \(myOtherObject) should be equal")
}
XCTAssertEqualObjects is no longer available in Swift since the language makes no distinction between scalars and objects.
So we have to use XCTAssertEqual which leads to the following error:
"Type MyObject does not conform to protocol Equatable"
The only workaround I have found is to inherit (MyObject) from NSObject so that I can do the following:
XCTAssert(myObject == myOtherObject, "\(myObject) and \(myOtherObject) should be equal")
So my question is: Is there a way (as of beta 5) to use XCTAssertEqual for custom types without having to rely on NSObject or littering all custom types with "==" overloads ?
If you want to compare references in Swift, you can use === operator. This is what happens when you subclass from NSObject (when you are comparing objects in Objective-C using XCTAssertEqual, you are comparing references).
But is this really what you want?
class MyObject {
var name: String
init(name: String) {
self.name = name
}
}
func testMyObjectsEqual() {
let myObject = MyObject(name: "obj1")
let myOtherObject = MyObject(name: "obj1")
let otherReferenceToMyFirstObject = myObject
XCTAssert(myObject === myOtherObject) // fails
XCTAssert(myObject === otherReferenceToMyFirstObject) // passes
}
I guess that when you are comparing custom objects, you probably should make them conform to the Equatable protocol and specify, when your custom objects are equal (in the following case the objects are equal when they have the same name):
class MyObject: Equatable {
var name: String
init(name: String) {
self.name = name
}
}
func ==(lhs: MyObject, rhs: MyObject) -> Bool {
return lhs.name == rhs.name
}
func testMyObjectsEqual()
{
let myObject = MyObject(name: "obj1")
let myOtherObject = MyObject(name: "obj1")
XCTAssertEqual(myObject, myOtherObject) // passes
}
Since Xcode 12.5 there is XCTAssertIdentical

swift optional chaining with cast

The code below highlights a problem I'm having combining optional chaining and casts with Apple's swift language
import Foundation
import CoreData
class MyExample {
var detailItem: NSManagedObject?
func example() {
//In the actual implementation it is assigned to a UITableViewCell textLabel.text with the same result.
let name: String = self.detailItem?.valueForKey("name") as String
}
}
The above results in:
'AnyObject' is not convertible to 'String'
I am able to do this by making the class variable an implicitly unwrapped optional as follows:
class MyExample2 {
var detailItem: NSManagedObject!
func example() {
let name: String = self.detailItem.valueForKey("name") as String
}
}
This works, but now this variable doesn't reflect my real world need for it to be optional
This next example also works with detailItem as optional:
class MyExample3 {
var detailItem: NSManagedObject?
func example() {
let name: String = self.detailItem?.valueForKey("name").description as String
}
}
The problem with the above is I had to use description. It's generally not a good idea to use output from this function to show to the user (according to Apple, and just common sense)
The above also only works because I am looking for a string. If I needed an object, it wouldn't work.
As a point of interest this example throws a different error:
class MyExample4 {
var detailItem: NSManagedObject?
func example() {
let name: String = self.detailItem?.valueForKey("name")
}
}
The above throws:
Could not find member 'valueForKey'
NSmanagedObject clearly has a valueForKey.
Trying one more thing, I discovered a potential solution:
class MyExamplePotentialSolution {
var detailItem: NSManagedObject?
func example() {
let name: NSString = self.detailItem?.valueForKey("name") as NSString
}
}
The problem with the above, is it doesn't work when actually assigned to a UITableViewCell detailTextLabel.text attribute.
Any ideas?
Updated Answer
The simplest real world usage turned out to be this:
cell.detailTextLabel.text = self.detailItem?.valueForKey("name") as? NSString
The key is AnyObject can't be cast to a native swift type directly. As the accepted answer showed, it can be cast as a native Swift string from NSString. It just isn't necessary in this case.
There are 2 problems: the first is that in this line:
let name: String = self.detailItem?.valueForKey("name") as String
the right part is an optional (detailItem is optional), so whatever the expression returns, it cannot be assigned to a non-optional variable having type String
The second problem is that valueForKey returns AnyObject!, which can be an instance of any class - but String is not a class, you'd need Any in order to be able to cast that into a String.
I presume that NSManagedObject returns an NSString, so you can achieve what you need with this line:
let name: String? = (self.detailItem?.valueForKey("name") as? NSString) as? String

Resources