Checking if [NSObject : AnyObject]! is NSNull in Swift - objective-c

My project uses both Swift and Objective C. I have a singleton written in Objective C which as a property of the type NSDictionary.
#property(nonatomic, strong) NSDictionary *currentDictionary;
This is an old class that's being used throughout my project. I am trying to use this class in a Swift class like this
if let dict = DataManager.sharedManager().currentDictionary
{
//
}
The problem i am facing is that currentDictionary is being set using data from the server. At times this might be
In Objective C classes, i can handle the situation with the following check
if ([currentDictionary isKindOfClass: [NSNull class]])
{
// Do nothing
}
But i am not sure how to implement a similar check in Swift. I tried the following
if let data = DataManager.sharedManager().currentDictionary as? NSNull
{
return
}
But it doesn't work and also i get a compiler warning :
"Cast from "[NSObject : AnyObject]!" to unrelated type "NSNull" always fails"
This is different from checking for Null values within the dicitonary as they will be 'AnyObject's and i can try casting them into the type i want to check.
Can someone please provide any pointers on how to handle this situation properly

First of all, if the variable can contain something else that NSDictionary, don't set its type to NSDictionary. Swift is type safe and it will trust the declared type.
The easiest workaround would be to make it id in Objective-C.
Then in Swift you can simply:
guard let data = DataManager.sharedManager().currentDictionary as? NSDictionary else {
return
}
If you can't change the original code, just create a Swift accessor with correct type using a category, e.g.
#interface DataManager (Swift)
// solution 1, will be converted to AnyObject in Swift
- (id)currentDictionaryForSwift1;
// solution 2, let's handle NSNull internally, don't propagate it to Swift
- (NSDictionary *)currentDictionaryForSwift2;
#end
#implementation DataManager
- (id)currentDictionaryForSwift1 {
return self.currentDictionary;
}
- (NSDictionary *)currentDictionaryForSwift2 {
if (self.currentDictionary == [NSNull null]) {
return nil;
}
return self.currentDictionary;
}
#end
I would recommend you to handle NSNull internally. There should be no need to for other code to handle nil and NSNull separately.
You could actually solve it already in the getter:
- (NSDictionary *)currentDictionary {
if (_currentDictionary == [NSNull null]) {
return nil;
}
return _currentDictionary;
}
or in the setter
- (void)setCurrentDictionary:(NSDictionary *)currentDictionary {
if (currentDictionary == [NSNull null]) {
_currentDictionary = nil;
} else {
_currentDictionary = currentDictionary;
}
}
As you can see, there are multiple solutions but the best solution should improve even your Obj-C code. The difference between NSNull and nil should be handled locally and not propagated.

If you want to validate wether currentDictionary is nil or not, you can use:
guard let currentDictionary = DataManager.sharedManager().currentDictionary else {
return
}
Replace guard-else statement with if-else if you don't want to return early.
If you want to validate contents of currentDictionary is NSNull or not:
if let value = DataManager.sharedManager().currentDictionary["key"] {
// do something with value
}

Related

Check object if it's equal to nil or to [NSNull null]

if object set to nil and I want to check for its value.
if object != nil {
// do something
}
or
if object != [NSNull null] {
// do something
}
I guess the second condition will be triggered but I am not sure why.
Can someone explain please?
As others have pointed out, object != nil && object != NSNull.null would give you the expected behaviour, however it might be tedious and error prone to write this pair of conditions every time.
Alternatively you can use inverted logic, by adding an nonNullValue method to virtually almost all objects in the system:
// in some header file
#interface NSObject(NullExtension)
- (instancetype)nonNullValue;
#end
// in some implementation file
#implementation NSObject(NullExtension)
// regular objects are not null (right?)
- (instancetype)nonNullValue {
return self;
}
#end
#implementation NSNull(NullExtension)
// let's make NSNull report as being null
- (instancetype)nonNullValue {
return nil;
}
#end
// adding this just for the sake of completeness, it's highly unlikely
// that JSON's will decode NSProxy instances
#implementation NSProxy(NullExtension)
- (instancetype)nonNullValue {
return self;
}
#end
you can then simply use it on your pointers:
// since we're in Objective-C, a nil pointer will always fail the check
// and thanks to the NSNull extension, NSNull instances will behave the same
if ([object nonNullValue] != nil) {
// NSNull won't get here
// nils won't get here
// other objects will get here
}
This approach is a little bit invasive as it touches all NSObject subclasses, however it eliminates the need of writing multiple conditions.
If you want to make sure object isn't nil and it isn't NSNull null then do:
if (object && object != [NSNull null]) {
// do something with object
} // else it's either nil or NSNull null
The difference between [NSNull null] and nil is that nil is an empty object that has completely disappeared from memory, and we use [NSNull null] when we want to express the idea that "we need a container that has nothing in it," which is "an object whose value is null." If you look up the development documentation you'll see that the class NSNull inherits NSObject and has only one "+ (NSNull *) null;" Class methods. This means that the NSNull object has a valid memory address, so any reference to it in the program will not cause the program to crash.

How to find the type of array in swift or objective c

I am facing a issue where I need to find the type of property defined in a class.
For Ex:
class Demo {
var employee: [String] = [String]()
var departments: [Int] = [Int]()
}
I am able to find that employee or departments is of type Array. However, I am not able to find out which kind of array the variable is. Is it a array of Strings or Numbers?
Thanks for the help.
In Objective-C arrays are untyped at runtime. So they can't tell you their object type because they don't know it. This doesn't change with lightweight generics; they allow the compiler to perform some checks by declaring intended usage but leave no runtime footprint.
Get object type from empty Swift Array tackles the Swift side of things; I couldn't directly speak to that from memory. If it's correct then it looks like it could do with some votes.
you can make type checking for objects like
for(id object in departments)
{
if([object isKindOfClass:[NSString class]])
{
//NSString here
}
else if([object isKindOfClass:[NSNumber class]])
{
//NSNumber here
}
}
or if you want an object of responding to selector
for(id object in departments)
{
if([object respondsToSelector:#selector(methodName:)])
{
//object responds to selector here
}
}
Improving meth's answer in swift just like objective-c you can check for type of an item in the array
for item in library {
if item is String {
//Do something with Strings
} else if item is Int {
//Do something with Numbers
}
}
You can find all the documentation for swift Type Casting here.

How to test if a #property from ObjC object is nil in Swift?

I have a model object written in Objective-C, which has a property of the type NSMutableArray.
It can either be nil or has a valid object reference.
I am using bridging header and I have a few files written in Swift.
In the Swift file I want to iterate through the objects in the array, only if it has something in it.
How can I achieve this?
I have tried things such as:
if let a = MyObj.myArray {
}
if(MyObj.myArray != nil) {
}
if(MyObj.myArray != NSNull()) {
}
I finally got the hang of optionals in Swift code alone, but I am not able to understand the behavior when I am passing around objects written in Objective-C in Swift code.
My Actual Code Looks like this :
Code :
if let values = attribute.values {
for val in values {
print(val);
}
return true;
}
Exception:
-[NSNull countByEnumeratingWithState:objects:count:]: unrecognized selector sent to instance
The error message tells us the problem:
-[NSNull countByEnumeratingWithState:objects:count:]: unrecognized selector sent to instance
Your attribute.values isn't nil or an NSMutableArray. It's [NSNull null].
NSNull is a real object that's used instead of nil in collections, because the common Foundation collections (NSArray and NSDictionary) cannot store nil as a value.
We most often come across NSNull when decoding a data structure from JSON. When the JSON decoder finds null (a JSON literal) in an array or object, it stores [NSNull null] in the corresponding NSArray or NSDictionary.
Something like this should work:
if let values = attribute.values where values != NSNull() {
for val in values {
print(val);
}
return true;
}
You should use the new Nullability annotation syntax for your Objective-C properties. These annotations help communicate to Swift whether you intend for an object to be nil or not. For example:
#property (nullable) NSMutableArray* myArray;
With these annotations, your Objective-C objects should work just like a native Swift object. You can do an if let or any other nil check.
if let arr = myObject.myArray {
// Do something with arr
}
else {
// Object is nil do something else
}
if myObject.myArray == nil {
// Array is nil, handle it.
}
You can read more about Nullability annotations for Objective-C at the Apple Swift blog.
https://developer.apple.com/swift/blog/?id=25

Issues with custom getters and setters to retrieve and store values into a data dictionary

I'm having issues with my app crashing when getting/setting primitive data types and instance methods don't run and only return null values.
I am trying to implement a system that allows me to create an object for subclassing use, mark some properties with the #dynamic tag, and have those dynamic properties set their values into and retrieve their values from a dictionary of data.
Currently in my original object I used the following methods to override the getting of properties
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
NSString *sel = NSStringFromSelector(selector);
if ([sel rangeOfString:#"set"].location == 0) {
return [NSMethodSignature signatureWithObjCTypes:"v#:#"];
} else {
return [NSMethodSignature signatureWithObjCTypes:"##:"];
}
return [super methodSignatureForSelector:selector];
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
NSString *key = NSStringFromSelector([invocation selector]);
key = [key lowercaseString];
if ([key rangeOfString:#"set"].location == 0) {
key = [key substringWithRange:NSMakeRange(3, [key length]-4)];
__unsafe_unretained id obj;
[invocation getArgument:&obj atIndex:2];
[self.data setObject:obj forKey:key];
} else {
id obj = [self.data objectForKey:key];
if (obj) {
[invocation setReturnValue:&obj];
}
}
}
This has worked almost perfectly, however when I try to set and store and ints, longs, or BOOLs, my application crashes with the following error:
Thread 1: EXC_BAD_ACCESS (code=1, address = 0xd)
When I use instance methods, they simply aren't run. Any logs I put in them don't output to the console, and all return values default to null. I understand this to be because I overwrote message forwarding, however it is the only way I found to implement this data structure system.
Additionally, I initially thought that the message overriding would only affect properties that I declared #dynamic, however I have come to understand that this is not the case.
If anyone could help me with the primitive data crashes, the instance method resolution, and having this selector only apply to properties I define, I would be greatly in their debt.
Thank you for your time even if you chose not to or are unable to help me.

Conforming to a list of Protocols

I have a condition where I want a view controller to conform to any of 4 protocols.
Is there a way to check if it conforms to any of these 4 protocols without doing a bunch of or statements in my if?
Can you make an array of protocols?
Sure, you can make an array of protocols:
NSArray *protocols = #[#protocol(UIApplicationDelegate),
#protocol(UIImagePickerControllerDelegate),
#protocol(UIScrollViewDelegate),
#protocol(NSFileManagerDelegate)];
You could then check that some object conforms to all of them:
UIViewController *vc = ...;
for (Protocol *protocol in protocols) {
if (![vc conformsToProtocol:protocol]) {
NSLog(#"object doesn't conform to %#", protocol);
}
}
It's hard to imagine why you'd want to do this at run-time, though.
Perhaps what you really want is to declare that something conforms to several protocols. You can do that too, and the compiler will check it for you at compile-time. For example:
#property (nonatomic, strong) id<UIApplicationDelegate, UIImagePickerController,
UIScrollViewDelegate, NSFileManagerDelegate> swissArmyKnife;
If you try to assign something to this property, and it doesn't conform to all four protocols, the compiler will issue a warning.
Perhaps what you are saying is you want to verify that an object conforms to at least one of the protocols, but that it doesn't have to conform to all of them. In that case, you have to check at run-time. But that smells like a bad design to me.
If you want to send a message to the object, but you're not sure that it will understand the message, it's probably better to check specifically for the message you want to send, instead of checking for protocol conformance.
// This is probably not such a good idea.
if ([object conformsToProtocol:#protocol(NSFileManagerDelegate)]) {
return [object fileManager:myFileManager shouldRemoveItemAtURL:url];
} else {
return arc4random_uniform(2);
}
// This is probably better.
if ([object respondsToSelector:#selector(fileManager:shouldRemoveItemAtURL:)]) {
return [object fileManager:myFileManager shouldRemoveItemAtURL:url];
} else {
return arc4random_uniform(2);
}
Sure.
id object = ...;
Protocol *protocols[] = {#protocol(Blah), #protocol(Foo), #protocol(Bar)};
for(unsigned i = 0; i < sizeof(protocols) / sizeof(Protocol *); ++i) {
if([object conformsToProtocol:protocols[i]]) {
//do something...
break;
}
}