Ad

Our DNA is written in Swift
Jump

Radar: CGRectMakeWithDictionaryRepresentation

This is one of those rare jewels of a bug that will cost you days to figure out if you encounter it in the context of a large app. It makes you doubt our own sanity until you come to the painful conclusion that the problem indeed resides in Apple’s code, not yours.

In this special case we had a couple of rare circumstances that worked together to form a scenario where CGRectMakeWithDictionaryRepresentation partially fails to reconstitute a CGRect from a dictionary. This function is literally ancient, it exists since iOS 2 and Mac OS 10.5. This makes it even more implausible that nobody has stumbled across this before us.

In the project where we first saw the problem these where the steps that led to this bug’s discovery:

  1. Create a CGRect that is not an integer
  2. Write a dictionary from iOS simulator which contains a dictionary encoding this CGRect
  3. Open this dictionary in Xcode’s property list editor
  4. Upon saving some of the least significant digits change in the “real” items
  5. This new dictionary can no longer be parsed on iOS

What’s even funnier is that some such modified values can still be read, but then the function fails internally and the remaining values don’t get parsed, i.e. stay zero.

From what I have seen researching this bug looks like certain floating point numbers cannot be represented on iOS. The normal parsing functions are able to round to the closest value that can be represented in 32-bit floats, whereas CGRectMakeWithDictionaryRepresentation fails to do so. The first value that cannot be exactly represented is truncated, all following values turn out to be Zero.

This was filed as rdar://12358120 and on OpenRadar.

CGRectMakeWithDictionaryRepresentation

Summary

CGRectMakeWithDictionaryRepresentation on iOS fails to parse certain float values from a plist saved on Mac.

This bug is a very rare occurrence because it only appears if you have non-integer values encoded in a property list, modify this on Mac (e.g. Xcode) and then try to parse it again on iOS.

Steps to Reproduce

  1. on iOS: create a CGRect (123.2112731933594,123.2112731933594,123.2112731933594,123.2112731933594)
  2. save it into a file, notice that the value will be 123.21127319335938 instead.
  3. open it with Xcode plist editor
  4. save it, the values in the file will change to be 123.2112731933594
  5. load the dictionary from disk, try to extract the CGRect by CGRectMakeWithDictionaryRepresentation

Expected Results

the CGRect values should be 123.21127319335938 or 123.2112731933594

Actual Results

only origin gets a truncated value, all other values are zero: {{123.211, 0}, {0, 0}}, return value of the function is FALSE which means an error occurred.

Regression

n/a

Notes

A sample project is provided that demonstrates the issue in the form of unit tests:

  • testOriginalPlist parses an iOS generated plist without issue
  • testMofiedPlistByXCode parses a plist modified by Xcode showing the failure
  • testParseRealString shows that the problematic representation works with float/doubleValue
  • testRectMake demonstrate how modifying a fresh encoded number fails if the 5938 (iOS) is changed to 594 (Mac)

NSDictionaryCGRectParsing Sample Project


Categories: Bug Reports

1 Comment »

  1. Seems like it fails to de-serialize all kinds of values, I use a JSON-File as cache file (NSKeyedArchiver was too slow) and if the NSDictionary gets loaded from disk (Apple’s JSON implementation) CGRectMakeWithDictionaryRepresentation fails to deserialize the data it’s corresponding function CGRectCreateDictionaryRepresentation just wrote a few seconds ago.

    This only happens on the device, works fine in Simulator