1

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

humblePilgrim
  • 1,818
  • 4
  • 25
  • 47
  • 2
    The `NSDictionary` should ***never*** be of type `NSNull`. Its content may be, but not the dictionary itself. – luk2302 May 10 '16 at 09:53
  • 1
    This post is probably useful for you http://stackoverflow.com/questions/24026609/detect-a-null-value-in-nsdictionary – HSG May 10 '16 at 09:55
  • @luk2302 .. Yes , it should not be. I have communicated the same with the server side. But i would like to add a safety check at the client as well. – humblePilgrim May 10 '16 at 10:16
  • As @luk2302 says: If there is a problem, you should have nil, but **NEVER EVER** NSNull. – Eiko May 10 '16 at 10:41
  • There is nothing to communicate. It **MUST NOT** be `NSNull`. There is a reason the type is specified! The *only* option for you is to parse the response you receive from the server and decide wether or not it is actual content -> put it inside the dictionary **or** if it is `null`, `nil` or whatever you either create an empty dictionary or set the dictionary to `nil`, you **do not** set the dictionary *itself* to `NSNull`. The same way you do not set it to literally `@"no value present"` or `@(-2)`. – luk2302 May 10 '16 at 10:45

2 Answers2

2

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.

Sulthan
  • 128,090
  • 22
  • 218
  • 270
1

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
}
mro
  • 191
  • 2