How to read property list (binary format) if the iOS fails to load it via NSPropertyListSerialization

In my App I work with a "tree" structure (nested NSArrays and NSDictionaries) and have stored them in the file system using NSPropertyListSerialization, which always works fine.

In general loading the same structure via NSPropertyListSerialization works fine too, but if the "tree" is getting larger (nested deeper), NSPropertyListSerialization can no longer read the file (even if it was able to saved it without error before).
It will fail with the error "Too many nested arrays or dictionaries".

The issue seems to be the nesting level, not the general amount of nodes in the property list, because the file itself is very small (about 50 KB) .

I've never seen this in the past, so this might be a new issue of iOS 14.

So my question is: is there another way to read the propertylist file? The problem is that the file is stored in the binary format (not the plain XML format), so just using a general XML parser is not an option, the binary file format seems to be undocumented by Apple.

Accepted Reply

I hope you will find useful information here. It notably details Binary plist structure.

https ://medium. com/@karaiskc/understanding-apples-binary-property-list-format-281e6da00dbd

Have you tried to read the binary in a text editor ?

Note: the article dates 2018, so not iOS 14. So there may have been some changes, but I assume (hope) it has remained essentially the same.

Good luck. Thanks to feedback.

Replies

I hope you will find useful information here. It notably details Binary plist structure.

https ://medium. com/@karaiskc/understanding-apples-binary-property-list-format-281e6da00dbd

Have you tried to read the binary in a text editor ?

Note: the article dates 2018, so not iOS 14. So there may have been some changes, but I assume (hope) it has remained essentially the same.

Good luck. Thanks to feedback.
I tried this here in my office and I don’t hit your limit. Specifically, this code:

Code Block
let root = NSMutableDictionary()
var parent = root
for i in 0..<60 {
let child = NSMutableDictionary()
parent["n\(i)"] = child
parent = child
}
let d = try! PropertyListSerialization.data(fromPropertyList: root, format: .binary, options: 0)
let root2 = try! PropertyListSerialization.propertyList(from: d, options: [], format: nil)
print(root2)


prints this:

Code Block
{
n0 = {
n1 = {
n2 = {
n3 = {
n4 = {
n5 = {
… and so on, all the way to n59 …
};
};
};
};
};
};
}


This is Xcode 12.4 running on macOS 11.2.1

Any idea what you’re doing differently?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
I could have reproduced the issue using @eskimo's code with changing 60 to 600.
(Just my curiosity, why do you need this deeply nested data?)

Some other methods, such as NSDictionary.init(contentsOf:) or CFPropertyListCreateWithData, seem to be using the common code with PropertyListSerialization and causes the same issue.

is there another way to read the propertylist file?

I'm afraid you may need to parse the binary file using the info shown in @Claude31's reply.
I tested in playground (Xcode 12.4).

Works with 128.

Fails with 129, with a different error message:
Fatal error: 'try!' expression unexpectedly raised an error: Error Domain=NSCocoaErrorDomain Code=3840 "Unexpected character b at line 1" UserInfo={NSDebugDescription=Unexpected character b at line 1, kCFPropertyListOldStyleParsingError=Error Domain=NSCocoaErrorDomain Code=3840 "Conversion of string failed."


with a different error message:

Unfortunately, Playground disposes some sort of error messages. Better try with Command Line Tools project.
@eskimo:
Actually the depth of my tree is only around 65 levels, but because each node (NSDictionary) uses an array (NSArray) to store its child nodes, the depth level when counting dictionaries and arrays separately reaches the 128 limit which @Claude31 has mentioned. So for a tree structure this is not that deep at all, and I would expect that the iOS should not have any issues with it. Don't you think so?


@all
I have already found information about the file format, so I am now able to parse the file myself. It's actually pretty easy. Thanks for all the responses so far.


You should file a bug report (at least against documentation, requesting to mention the limit), with the simple code eskimo has provided and the 128 limit.

Please report the FB number and don't forget to close the thread.
@Claude31
I filed a bug report already: FB9036815

I could have reproduced the issue using @eskimo's code with changing
60 to 600.

Some more tweaking reveals that the limit is 512. With my test code, changing the 60 to 512 works but changing it to 513 fails.

Honestly, I’m not surprised. That’s a pretty deep limit and property lists were never intended to cover all data storage use cases; rather, they’re supposed to be an way to store simple data. IMO this is definitely constitutes managerial abuse and I’d recommend that you explore other serialisation strategies.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
@eskimo
In playground, Xcode 12.4, I found the limit at 128.

Both 128 and 512 are power of 2, so that looks like a design feature.