I tried to create an ObservableObject with non array @Published item. However, I still don't know how to do so. I tried to use a ? to do so. But when I display it in view like Text((details.info?.name)!)
, and it return Thread 1: Swift runtime failure: force unwrapped a nil value
I don't know what the problem and how to solve. Is it my method of creating observable object class are correct?
class ShopDetailJSON: ObservableObject {
@Published var info : Info?
init(){load()}
func load() {
URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else {
print("No data in response: \(error?.localizedDescription ?? "Unknown error").")
return
}
if let decodedShopDetails = try? JSONDecoder().decode(ShopDetail.self, from: data) {
DispatchQueue.main.async {
self.info = decodedShopDetails.info!
}
} else {
print("Invalid response from server")
}
}.resume()
}
}
struct Info : Codable, Identifiable {
let contact : String?
let name : String?
var id = UUID()
enum CodingKeys: String, CodingKey {
case contact = "contact"
case name = "name"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
contact = try values.decodeIfPresent(String.self, forKey: .contact)
name = try values.decodeIfPresent(String.self, forKey: .name)
}
}
struct ShopDetail : Codable {
let gallery : [Gallery]?
let info : Info?
enum CodingKeys: String, CodingKey {
case gallery = "gallery"
case info = "info"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
gallery = try values.decodeIfPresent([Gallery].self, forKey: .gallery)
info = try values.decodeIfPresent(Info.self, forKey: .info)
}
}
Sample JSON data
{
"gallery": [],
"info": {
"contact": "6012345678",
"name": "My Salon",
}
}
This is answer is a bit of a guess as to what happens in your code, but if the JSON data is never null, as you say in the comments, it's likely that you're trying to access a not-yet-updated
ShopDetailJSON.info
optional property in your view.First, some clean-up. You don't need to the custom implementation of
init(from:)
- just conforming toCodable
is enough in your case. And if the JSON values aren't optional, no need to make them into an optional type:Then, when you get the JSON you wouldn't need to deal with optionals and force-unwrap
!
(which should have been avoided anyways):In the view, you need to check that the
info
property is notnil
before accessing its elements.