My plist-reader works well with a specific structure, but crashes when I try to make it universal. Here is a specific structure edition (works well):
struct Auth0_: Codable {
let domain: String
let clientId: String
}
extension Bundle {
func decodePlist(plistName: String) -> Auth0_ {
guard let path = Bundle.main.url(forResource: plistName, withExtension: "plist") else {
fatalError("There is no such plist-file \(plistName).")
}
guard
let data = try? Data(contentsOf: path),
let dict = try? PropertyListDecoder().decode(Auth0_.self, from: data)
else {
fatalError("Cannot convert plist \(plistName) to JSON.")
}
return dict
}
}
let dict = Bundle.main.decodePlist(plistName: "Auth0")
Then I try to make function <T: Codable>
and get the fatal error
Cannot convert plist...
struct Auth0_: Codable {
let Domain: String
let ClientId: String
}
extension Bundle {
func decodePlist<T: Codable>(plistName: String) -> T {
guard let path = Bundle.main.url(forResource: plistName, withExtension: "plist") else {
fatalError("There is no such plist-file \(plistName).")
}
guard
let data = try? Data(contentsOf: path),
let dict = try? PropertyListDecoder().decode(T.self, from: data)
else {
fatalError("Cannot convert plist \(plistName) to JSON.")
}
return dict
}
}
let dict: [Auth0_] = Bundle.main.decodePlist(plistName: "Auth0")
Where is the mistake?
I guess you haven't changed your plist content in the two codes.
You have two lines of cause that can cause the fatalError
.
guard
let data = try? Data(contentsOf: path),
let dict = try? PropertyListDecoder().decode(T.self, from: data)
else {
fatalError("Cannot convert plist \(plistName) to JSON.")
}
Debugging start to find the reason, so let's split them:
guard let data = try? Data(contentsOf: path) else {
fatalError("Cannot convert read plist data \(plistName).")
}
guard let dict = try? PropertyListDecoder().decode(T.self, from: data) else {
fatalError("Cannot convert plist \(plistName) to JSON.")
}
Now, we can see that's the second line that is causing the issue.
Now, I'd strongly suggest to NEVER
use try?
(with a question mark) when you don't know how to debug it. Use a proper do
/try
/catch
:
do {
let dict = try PropertyListDecoder().decode(T.self, from: data)
} catch {
fatalError("Cannot convert plist \(plistName) to JSON error: \(error)")
}
Now, you should get an error about decoding issue, with explicit info:
typeMismatch(Swift.Array<Any>,
Swift.DecodingError.Context(codingPath: [],
debugDescription: "Expected to decode Array<Any> but found a dictionary instead.",
underlyingError: nil))
So it says that at the top level (codingPath
being empty), it expects an array, but found a dictionary instead.
Why is that happening?
Because when you wrote let dict: [Auth0_] = Bundle.main.decodePlist(plistName: "Auth0")
, it then did let dict = try? PropertyListDecoder().decode([Auth0_].self, from: data)
, which is different from what you used before when the code was specific and not generic.
So it should be:
let dict: Auth0_ = Bundle.main.decodePlist(plistName: "Auth0")
Now, if we look at your code with error, you wrote let dict: [Auth0_] = ...
, but [Auth0_]
, that's Array<Auth0_>
, so you expected it to have an array (ie a list), of Auth0_
, but you have one only, and that's not a list of one element. So the naming was misleading at start.
Side note:
You are just decoding, so you shouldn't need to restrict it to Codable
, but only Decodable
. Since Codable
is Encodable
+ Decodable
.