前情提要
在Xcode中下面的代码:
let missions: [Mission] = Bundle.main.decode("missions.json")
Text(mission.launchDate ?? "N/A")
显示如下的报错输出:
Cannot convert value of type 'String' to expected argument type 'Date'
Initializer 'init(_:)' requires that 'Date' conform to 'StringProtocol'
经排查原因为:Mission的launchDate为Date?类型,但Text需要一个String类型。
struct Mission: Codable, Identifiable {
struct CrewRole: Codable {
let name: String
let role: String
}
let id: Int
let launchDate: Date?
let crew: [CrewRole]
let description: String
var displayName: String {
"Apollo \(id)"
}
var image: String {
"apollo\(id)"
}
}
虽然launchDate是一个可选值,并且如果为空会返回“N/A”,但是即使有值时,也需要将Date转换为String类型,否则编译器会报错。
解决方案
我们只需要将Date格式为String类型后,就可以在Text()中显示输出。
因此,我们可以在Mission结构中添加如下代码:
var formattedLaunchDate: String {
launchDate?.formatted(date: .abbreviated, time: .omitted) ?? "N/A"
}
Formatted(date:time:)是Date类型的一个方法,是Swift中用于将Date类型格式化字符串的一种方法,用来格式化日期和时间。date: .abbreviated表示简短的日期格式,例如Oct 3, 2024。.time: .omitted表示不显示时间部分。
修改后的Mission结构:
struct Mission: Codable, Identifiable {
struct CrewRole: Codable {
let name: String
let role: String
}
let id: Int
let launchDate: Date?
let crew: [CrewRole]
let description: String
var displayName: String {
"Apollo \(id)"
}
var image: String {
"apollo\(id)"
}
// 将日期格式化为字符串
var formattedLaunchDate: String {
launchDate?.formatted(date: .abbreviated, time: .omitted) ?? "N/A"
}
}
然后,我们在Text()中直接调用新定义的formattedLaunchDate计算变量:
Text(mission.formattedLaunchDate)
我们的日期就可以正常显示。
问题得到解决。
延伸问题
如果你是在解码JSON文件的日期字段,按照上述操作后,Xcode报错:
Failed to decode missions.json from bundle due to type mismatch – Expected to decode Double but found a string instead.
那是因为JSONDecoder默认的日期解码策略和JSON文件中的日期格式不匹配导致的。默认情况下,JSONDecoder会尝试将日期解码为Double类型的时间戳格式(即Unix时间戳。表示自1970年以来的秒数)。
1728180541
但因我所读取的JSON文件中的日期是字符串格式。
"launchDate": "1968-10-11"
所以,JSONDecoder无法识别这些日期字符串,就导致类型不匹配的错误。
catch DecodingError.typeMismatch(_, let context) {
fatalError("Failed to decode \(file) from bundle due to type mismatch – \(context.debugDescription)")
}
因此,我们的解决方案就是让JSONDecoder使用正确的日期格式,可以通过自定义DateFormatter来解析JSON中的日期字符串。
解析代码为:
let decoder = JSONDecoder()
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd" // 解析的日期格式
decoder.dateDecodingStrategy = .formatted(formatter) // 告诉解码器使用这个格式来解析日期
设置完DateFormatter后,问题全部得到解决。