I am currently trying to decode Property list using PropertyListEncoder
and Swift 4 Codable
protocol.
I tried some basic types (Strings, Ints, ..) and these all works just fine, but I am not able decode URL. I read multiple articles on this topic and I am pretty sure this should just work. However, the following example fails the decoding with this error:
Expected to decode Dictionary<String, Any> but found a string/data instead.
I think this works correctly with .json
files and I didn't find any informations about different support for codable types in JSONDecoder
and PropertyListDecoder
. Could this be caused by parser incompatibility?
I am using Xcode 9.1 and Swift 4.0.2.
Sample .plist
:
<?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>web</key>
<string>https://link.to</string>
</dict>
</plist>
Sample Swift code:
struct Info: Codable {
let web: URL
}
func loadInfo() {
let propertiesDecoder = PropertyListDecoder()
let data = try! Data(contentsOf:
Bundle.main.url(forResource: "web", withExtension: "plist")!)
try! propertiesDecoder.decode(Info.self, from: data)
}
Thanks for any help!
You can store URLs in Property Lists, and use the default Decodable implementation.
Based on the implementation of the Decodable protocol init in the Swift standard library, you must store the URL as a dictionary in the Plist, in a key called "relative". Optionally, you can also include a nested URL dictionary named "base" in the dictionary. These two values will get passed to the URL(string: relative, relativeTo: base)
constructor during decoding.
For example:
<?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">
<array>
<dict>
<key>apiHost</key>
<dict>
<key>relative</key>
<string>https://example.com/api/v0</string>
</dict>
<key>otherKey</key>
<string>SomeValue</string>
</dict>
</array>
</plist>
This Plist will decode using the following:
struct PlistItem: Codable {
let apiHost: URL
let otherKey: String
}
guard let filePath = Bundle.main.url(forResource: "Data", withExtension: "plist") else { return }
do {
let data = try Data(contentsOf: filePath)
let decoder = PropertyListDecoder()
let values = try decoder.decode([PlistItem].self, data)
}
catch { }
As I can see the key web
has a value of type String
so we need to keep a matching type
But to get the string as a URL
then we need to add a computed variable and maybe to optimize it a little bit we can make the variable lazy i.e it will be calculated once when it's needed
The edited strut would look like this:
struct Info: Codable {
let web: String
lazy var url: URL? = { return URL(string: web) }()
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With