Third-party iOS libraries and datatypes



  • This one isn't Apple's fault, but rather the fault of the vendor of a third-party iOS framework we use so our app can communicate with server software from that same vendor. I've got an object that was received via web service from vendor's server suite, as a result of a user-defined search or query operation. This object has a number of attributes, all of which have a "value" property of type id (id is basically iOS's var type, it can be of any type and has no static type checking). There are some fields to tell you the datatype since the type will change at runtime depending on what dataset you're currently pulling from. In any case, on the data I'm currently developing against, there's a field that contains a double (according to the datatype field provided by vendor). Actually not a primitive double, but the wrapper type NSDecimalNumber. NSDecimalNumber, being a reference type, is nullable. So what does vendor do when this value is null? Instead of using a null pointer, they instead use a string with the text "<null>".

    So now I have a value I can convert to a double most of the time. Except that to null-check first I have to convert it to a string and then compare to "<null>". I think vendor assumed everyone using their framework would just print the value directly, but in our case we need to do some additional processing and formatting which varies depending on the datatype.

    Here's what I originally wrote and expected to work (C#-ified since Objective-C is not easy to read):

    @Code said:
    if (att.value == null)
    {
        // Value is null, so don't crash the app by using it.
    }
    else
    {
        double temp = att.value.doubleValue();
        // Do something useful with temp.
    }

    And the final code? (Still C#-ified)

    @Code said:
    if (att.value == null || (att.value != null && att.value is string && (string)att.value == "<null>"))
    {
        // Value is null, so don't crash the app by using it.
    }
    else if (att.value != null && att.value is NSDecimalNumber)
    {
        double temp = att.value.doubleValue();
        // Do something useful with the value.
    }
    else
    {
        // Something even WTF-er than expected happened, display an error and debug info so we can add another special block to handle it.
    }

    But then there's another level on top of that because I have to repeat this for every datatype the app will encounter! So basically:

    @Code said:

    if (att.expectedDataType == DataTypeString)

    {

        // This one isn't too bad.

    }

    else if (att.expectedDataType == DataTypeShort)

    {

        // Convoluted code from above but adapted for short integers.

    }

    else if (att.expectedDataType == DataTypeInt)

    {

        // Convoluted code from above but adapted for integers.

    }

    else if (att.expectedDataType == DataTypeSingle)

    {

        // Convoluted code from above but adapted for floats.

    }

    else if (att.expectedDataType == DataTypeDouble)

    {

        // Convoluted code from above but adapted for doubles.

    }

    else if (att.expectedDataType == DataTypeDateTime)

    {

        // Convoluted code from above but adapted for dates.

    }

    else

    {

        // Throw an exception because we just found yet another odd combination of runtime conditions.

    }

    Now I get to re-examine it all and try to find a shorter way to do it. It's just frustrating because between Apple and this vendor there are always weird things going on and it's always more difficult than it needs to be. Developing on this project is kinda like playing Minesweeper but without the numbers and flags. There are "else" clauses all over our codebase which "shouldn't be possible to hit" but eventually something does hit it and we add yet another block to handle it. This means parts of our app have 4-5 times the expected amount of code.



  • @mott555 said:

    Objective-C is not easy to read

    It never seemed hard to read to me.



  • @mott555 said:

    ...att.value != null && att.value is string...

    Does the ObjC instanceof check claim that null is an instance of whatever class you pass it? If so, ugh.

    Now I get to re-examine it all and try to find a shorter way to do it.
    Static method which takes a var, compares it to the literal string "<null>", and returns either the input value or null?


  • @mott555 said:

    I think vendor assumed everyone using their framework would just print the value directly.

    Well, there's the wtf in a nutshell right there, isn't it. I'm a relative amateur compared to the folks on here, and even I can see that that's bad design, assuming you're correct about the vendor's assumption of course.



  • Ugh. So as far as I can tell from debugging, NSString objects don't respond to the method isKindOfClass. Meaning [value isKindOfClass:[NSString class]] won't tell me that it's a string. Basically the C# equivalent of value is string always returning false if value is of type string. So I just wrote a ton of code that apparently doesn't work.



  • This just keeps getting better. Sometimes the value is an instance of NSNull. So, depending on the time of day, ambient light level, and planetary alignment, null values might be indicated by any of the following:

    • nil (aka null pointer or 0)
    • NSString with text "<null>"
    • Instance of NSNull


  • @mott555 said:

    @Code said:
    if (att.value == null || (att.value != null && att.value is string && (string)att.value == "<null>"))
    Ignoring everything else in your post, and the fact that this doesn't even work (your lastpost)... that second null-check is extraneous (assuming short-circuiting rules apply).



  • @mott555 said:

    Ugh. So as far as I can tell from debugging, NSString objects don't respond to the method isKindOfClass. Meaning [value isKindOfClass:[NSString class]] won't tell me that it's a string. Basically the C# equivalent of value is string always returning false if value is of type string. So I just wrote a ton of code that apparently doesn't work.

    I had to test this because it just sounded weird. NSString objects do in fact respond positively to [myString isKindOfClass[NSString class]];. Maybe the value object you mentioned isn't what you think it is.



  • @Smitty said:

    @mott555 said:

    Ugh. So as far as I can tell from debugging, NSString objects don't respond to the method isKindOfClass. Meaning [value isKindOfClass:[NSString class]] won't tell me that it's a string. Basically the C# equivalent of value is string always returning false if value is of type string. So I just wrote a ton of code that apparently doesn't work.

    I had to test this because it just sounded weird. NSString objects do in fact respond positively to [myString isKindOfClass[NSString class]];. Maybe the value object you mentioned isn't what you think it is.

    You are correct. It was detecting the string-encoded nulls and failing later on for nulls that were encoded differently. I didn't notice it was failing on a different attribute until later. It doesn't help that the isKindOfClass method does not always seem to work in the debugger console.



  • @mott555 said:

    id is basically iOS's var type, it can be of any type and has no static type checking

    Missed a wtf there - why on earth would they call a dynamic type which can accept any value an "id"? I'd expect that name to go to some sort of autoincremented integer or GUID type or something - was Apple afraid of being accused of "ripping off" Java or VB by calling it "object" or "variant" or something?



  • @ekolis said:

    @mott555 said:
    id is basically iOS's var type, it can be of any type and has no static type checking

    Missed a wtf there - why on earth would they call a dynamic type which can accept any value an "id"? I'd expect that name to go to some sort of autoincremented integer or GUID type or something - was Apple afraid of being accused of "ripping off" Java or VB by calling it "object" or "variant" or something?

    Maybe it's like the psychology term. The mind's id is unstructured, just like type-less data.



  • In your situation, I'd suggest the following:
    * there likely is a bottleneck method from the library through which you get that data (either a call or a delegate method); in the worst case if there isn't you can create such a bottleneck. Wrap that bottleneck in the following way (shown here for a delegate method, modify as appopriate for a straight call):

    - (void)library:(ZZWhatever*)libObject providesData:(id)obj
    {
        if (obj != nil)
        {
            if ([obj isKindOfClass:[NSString class]] && [obj isEqualToString:@"<null>"])
            {
                obj = nil;
            }
            else if ([obj isKindOfClass:[NSNull class]])
            {
                obj = nil;
            }
        }
    
        [self processObject:obj];
    }

    That way the code to "fold" these odd not-quite-nil cases is only in one place, instead of being once per datatype; per datatype you're only left with the case of handling nil.

    * As for the various expected types to handle, they all seem to me to be property list types to me, which means that at least you should be able to define sensible fallback processing in the ultimate "else". Heck, if worst comes to worst you always have -[NSObject description], though it's normally meant for debugging so make sure to mark it somehow (e.g. paint it in red if you're rendering to the user) so that you handle that case eventually.



  • @ekolis said:

    @mott555 said:
    id is basically iOS's var type, it can be of any type and has no static type checking

    It's not like C#'s var, it's like C#'s System.Object. var declares the variable to be of the type of the initializer, so you don't have to type it out, but is well typed. But ObjectiveC's id is any kind of object, i.e. like System.Object. And like C#, ObjectiveC has object and non-object types (the later are the C ones), so id means any object, not anything at all. Because ObjectiveC is C mixed with dynamic, object oriented, garbage collected language that has absolutely nothing in common with C.

    @ekolis said:

    Missed a wtf there - why on earth would they call a dynamic type which can accept any value an "id"? I'd expect that name to go to some sort of autoincremented integer or GUID type or something - was Apple afraid of being accused of "ripping off" Java or VB by calling it "object" or "variant" or something?

    It's the "identifier of object". Sure, it's silly.



  • @Bulb said:

    @ekolis said:
    @mott555 said:
    id is basically iOS's var type, it can be of any type and has no static type checking

    It's not like C#'s var, it's like C#'s System.Object. var declares the variable to be of the type of the initializer, so you don't have to type it out, but is well typed. But ObjectiveC's id is any kind of object, i.e. like System.Object. And like Java (and unlike C#), ObjectiveC has object and non-object types (the later are the C ones), so id means any object, not anything at all. Because ObjectiveC is C mixed with dynamic, object oriented, garbage collected language that has absolutely nothing in common with C.

    FTFY

    @ekolis said:

    Missed a wtf there - why on earth would they call a dynamic type which can accept any value an "id"? I'd expect that name to go to some sort of autoincremented integer or GUID type or something - was Apple afraid of being accused of "ripping off" Java or VB by calling it "object" or "variant" or something?

    Objective C: 1983

    <a href="http://en.wikipedia.org/wiki/Visual_Basic>Visual Basic (6 and earlier): 1991

    Java: 1995


  • BINNED

    @mott555 said:

    is an instance of NSNull.

    I'm curious, what's the use of this class?

    Is it a singleton? Does it have any useful members, super-classes/types etc.? Why would you ever need such a class instead of having a single NULL-like entity.

     



  • @topspin said:

    @mott555 said:

    is an instance of NSNull.

    I'm curious, what's the use of this class?

    Is it a singleton? Does it have any useful members, super-classes/types etc.? Why would you ever need such a class instead of having a single NULL-like entity.

     

    See also: System.Data.DBNull, which means "The data in the database was null, it's supposed to be like this, it's not an error"


Log in to reply