Open krzyzanowskim opened 6 years ago
cc @phausler
PropertyListSerialization cannot handle Any. __NSCFTypes should always be non bridged CF object refs.
Only the following types are permissible to PropertyListSerialization (note this is different than PropertyListDecoder btw).
Array, Dictionary, String, Data, Date, Int8, Int16, Int32, Int64, UInt8, UInt16, UInt32, UInt64, Int, UInt, Float, Double, Bool, NSArray, NSDictionary, NSString, NSData, NSDate, NSNumber, CFArray, CFDictionary, CFString, CFData, CFDate, CFNumber, and CFBoolean should be the only types that can exist in a property list. (it is worth noting that any of the collection types, ala Array or NSArray MUST ONLY contain property list types and Dictionaries and NSDictionaries MUST ONLY contain String/NSString/CFString keys and MUST ONLY contain property list values)
Now that all being said: it is quite possible via the Codable protocol to marshal any structured type into a decomposition that is represented by those types.
Perhaps it might be useful for a code-example of how you are getting this error.
This happens by the CFKeyedArchiverUID
which represent the reference inside object graph.
minimum error throwing code is like below
let somethingCloseToUid = ["CF$UID": 1]
let encoder = PropertyListEncoder()
/// Decoding error is thrown when outputformat is xml
encoder.outputFormat = .xml
let uidData = try encoder.encode(somethingCloseToUid)
///Expected to decode Dictionary<String, Any> but found __NSCFType instead. (Darwin)
///Expected to decode Dictionary<String, Any> but found _NSKeyedArchiverUID instead. (linux)
let dictionary = try PropertyListDecoder().decode([String:Int].self, from: uidData)
CFKeyedArchiverUID
is created by NSCoder
and can be find by below
class DummyBase: NSObject, NSSecureCoding {
class var supportsSecureCoding: Bool { true }
func encode(with coder: NSCoder) {
}
override init() {
super.init()
}
required init?(coder: NSCoder) {
super.init()
}
}
func createdByNSCoding() throws {
let nscodingData = try NSKeyedArchiver.archivedData(withRootObject: DummyBase(), requiringSecureCoding: true)
let plist = try PropertyListSerialization.propertyList(from: nscodingData, format: nil)
print(plist)
if let dictionary = plist as? [String:Any], let top = dictionary["$top"] as? NSDictionary, let root = top["root"] as? AnyObject {
#if canImport(Darwin)
let typeID = CFGetTypeID(root)
/// CFKeyedArchiverUID __NSCFType
print(CFCopyTypeIDDescription(typeID) as String, type(of: root))
#else
print(root)
#endif
let data = try PropertyListSerialization.data(fromPropertyList: root, format: .xml, options: 0)
/*
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CF$UID</key>
<integer>1</integer>
</dict>
</plist>
*/
let xmlString = String(decoding: data, as: UTF8.self)
}
}
Additional Detail from JIRA
| | | |------------------|-----------------| |Votes | 0 | |Component/s | Foundation | |Labels | Bug | |Assignee | None | |Priority | Medium | md5: 243c24973c4c93e917da850b1907c06dIssue Description:
The plist file can store NSObject aka AnyObject aka Any aka CFTypeRef aka NSCFType and SwiftFoundation NSKeyedArchiver can read those.
The PropertyListSerialization is able to handle that too. YET, there is no way to decode this value using Decodable and PropertyListDecoder.
error is: eg. "Expected to decode Data but found __NSCFType instead."
I wonder if is it possible to (any)
bitcast those to Any
treat as raw bytes (return Data)
anything is better than throw error.