1

I have this local JSON File, which contains title, start and end. I want to create dates from the start and end keys, but a String for the title, so I can create Event objects. Right now I have decoded everything into Strings. So I'm trying to create a custom init, but an error keeps showing up "Return from initializer without initializing all stored properties". Not sure what am I doing wrong

Here's my Event model and my JSONFile

struct Event: Decodable & Equatable {
    let title : String
    let start : Date
    let end : Date
    
    //Custom decoding init
    init(from decoder : Decoder) throws {
        
        let container = try decoder.singleValueContainer()
        let stringType = try container.decode(String.self)
        
        switch stringType {
        case "title":
            self.title = try container.decode(String.self)
        case "start":
            self.start = try container.decode(Date.self)
        case "end":
            self.end = try container.decode(Date.self)
        default:
            throw DecodingError.dataCorruptedError(in: container, debugDescription: "Not valid date \(stringType)")
        }
    }
}

JSON

 [{"title": "Evening Picnic", "start": "November 10, 2018 6:00 PM", "end": "November 10, 2018 7:00 PM"}, {"title": "Nap Break", "start": "November 8, 2018 12:30 PM", "end": "November 8, 2018 1:30 PM"}, {"title": "Football Game", "start": "November 3, 2018 6:00 PM", "end": "November 3, 2018 10:00 PM"}, {"title": "Evening Cookout with Friends", "start": "November 6, 2018 5:00 PM", "end": "November 6, 2018 10:00 PM"}, {"title": "Roller Derby", "start": "November 7, 2018 12:00 PM", "end": "November 7, 2018 2:30 PM"}, {"title": "Basketball Game", "start": "November 8, 2018 7:30 PM", "end": "November 8, 2018 10:30 PM"}, {"title": "Local Pub with Friends", "start": "November 1, 2018 7:30 PM", "end": "November 1, 2018 11:00 PM"}, {"title": "Dentist Appointment", "start": "November 10, 2018 1:45 PM", "end": "November 10, 2018 2:30 PM"}, {"title": "Free Donuts", "start": "November 9, 2018 3:00 PM", "end": "November 9, 2018 4:00 PM"}, {"title": "TV Show Marathon", "start": "November 9, 2018 4:30 PM", "end": "November 9, 2018 9:00 PM"}, {"title": "Lunch with Friends", "start": "November 8, 2018 11:30 AM", "end": "November 8, 2018 1:00 PM"}, {"title": "SF Coffee Festival","start": "November 6, 2018 6:00 PM","end": "November 6, 2018 9:00 PM"}, {"title": "Beer with Friends", "start": "November 9, 2018 8:00 PM", "end": "November 9, 2018 9:30 PM"}, {"title": "Yoga", "start": "November 1, 2018 6:00 PM", "end": "November 1, 2018 7:30 PM"}, {"title": "Rock Concert", "start": "November 7, 2018 6:30 PM", "end": "November 7, 2018 11:00 PM"}, {"title": "Lunch Meeting", "start": "November 9, 2018 12:30 PM", "end": "November 9, 2018 2:30 PM"}, {"title": "Bicycling with Friends", "start": "November 1, 2018 6:00 AM", "end": "November 1, 2018 9:30 AM"}, {"title": "Birthday Party", "start": "November 10, 2018 12:30 PM", "end": "November 10, 2018 8:30 PM"}, {"title": "Football Tailgate with John", "start": "November 3, 2018 6:00 PM", "end": "November 3, 2018 10:00 PM"}]
  • See [JSONDecoder.dateDecodingStrategy](https://developer.apple.com/documentation/foundation/jsondecoder/2895216-datedecodingstrategy), or write a custom decoding init. No need for a second struct. Anyway, this kind of date format will be pretty hard to decode. – Sulthan Nov 30 '21 at 20:50
  • I have already shown how to decode them as dates in your last [question](https://stackoverflow.com/questions/70164011/check-for-overlapping-dates-range-from-strings-swift) – Leo Dabus Nov 30 '21 at 20:58
  • I'm trying to create a custom decoding init, but I can't seem to get it right. – Fernando Ivan Perez Ruiz Nov 30 '21 at 21:34
  • 1
    find an example. Your code is not even close to what a custom decoding init should look like. – Sulthan Nov 30 '21 at 22:36
  • 2
    Again I have already shown how to decode your dates. The linked duplicate post in your other question has the exact same date format. – Leo Dabus Dec 01 '21 at 00:06
  • firstly JSON Files always starts and end with { } so you should keep that in mind and for converting your JSON data you can use JSONSerialization and make sure to make class or struct type Codable – Noor Ahmed Natali Dec 01 '21 at 06:02

2 Answers2

2

There are no different types, all values in all dictionaries have always the same type String.

Therefore singleValueContainer is the wrong approach, and a custom initializer is not needed at all.

To decode the string dates to Date just add an appropriate date decoding strategy

let jsonString = """
 [{"title": "Evening Picnic", "start": "November 10, 2018 6:00 PM", "end": "November 10, 2018 7:00 PM"}, {"title": "Nap Break", "start": "November 8, 2018 12:30 PM", "end": "November 8, 2018 1:30 PM"}, {"title": "Football Game", "start": "November 3, 2018 6:00 PM", "end": "November 3, 2018 10:00 PM"}, {"title": "Evening Cookout with Friends", "start": "November 6, 2018 5:00 PM", "end": "November 6, 2018 10:00 PM"}, {"title": "Roller Derby", "start": "November 7, 2018 12:00 PM", "end": "November 7, 2018 2:30 PM"}, {"title": "Basketball Game", "start": "November 8, 2018 7:30 PM", "end": "November 8, 2018 10:30 PM"}, {"title": "Local Pub with Friends", "start": "November 1, 2018 7:30 PM", "end": "November 1, 2018 11:00 PM"}, {"title": "Dentist Appointment", "start": "November 10, 2018 1:45 PM", "end": "November 10, 2018 2:30 PM"}, {"title": "Free Donuts", "start": "November 9, 2018 3:00 PM", "end": "November 9, 2018 4:00 PM"}, {"title": "TV Show Marathon", "start": "November 9, 2018 4:30 PM", "end": "November 9, 2018 9:00 PM"}, {"title": "Lunch with Friends", "start": "November 8, 2018 11:30 AM", "end": "November 8, 2018 1:00 PM"}, {"title": "SF Coffee Festival","start": "November 6, 2018 6:00 PM","end": "November 6, 2018 9:00 PM"}, {"title": "Beer with Friends", "start": "November 9, 2018 8:00 PM", "end": "November 9, 2018 9:30 PM"}, {"title": "Yoga", "start": "November 1, 2018 6:00 PM", "end": "November 1, 2018 7:30 PM"}, {"title": "Rock Concert", "start": "November 7, 2018 6:30 PM", "end": "November 7, 2018 11:00 PM"}, {"title": "Lunch Meeting", "start": "November 9, 2018 12:30 PM", "end": "November 9, 2018 2:30 PM"}, {"title": "Bicycling with Friends", "start": "November 1, 2018 6:00 AM", "end": "November 1, 2018 9:30 AM"}, {"title": "Birthday Party", "start": "November 10, 2018 12:30 PM", "end": "November 10, 2018 8:30 PM"}, {"title": "Football Tailgate with John", "start": "November 3, 2018 6:00 PM", "end": "November 3, 2018 10:00 PM"}]
"""

struct Event : Decodable {
    let title : String
    let start, end : Date
}

let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "MMMM dd, yyyy h:mm a"

do {
    let decoder = JSONDecoder()
    decoder.dateDecodingStrategy = .formatted(formatter)
    let result = try decoder.decode([Event].self, from: Data(jsonString.utf8))
    print(result)
} catch {
    print(error)
}
vadian
  • 274,689
  • 30
  • 353
  • 361
  • Oh awesome!! I did try that and it work, in fairly new to swift so some of these concepts are still hard to grasp for me. But thanks a lot! Seriously I can’t express enough how grateful I am! – Fernando Ivan Perez Ruiz Dec 01 '21 at 15:46
0

-> firstly you need to accept proper JSON Data

let json = """
{
      "data" : [{"title": "Evening Picnic", "start": "November 10, 2018 6:00 PM", "end": "November 10, 2018 7:00 PM"}, {"title": "Nap Break", "start": "November 8, 2018 12:30 PM", "end": "November 8, 2018 1:30 PM"}, {"title": "Football Game", "start": "November 3, 2018 6:00 PM", "end": "November 3, 2018 10:00 PM"}, {"title": "Evening Cookout with Friends", "start": "November 6, 2018 5:00 PM", "end": "November 6, 2018 10:00 PM"}, {"title": "Roller Derby", "start": "November 7, 2018 12:00 PM", "end": "November 7, 2018 2:30 PM"}, {"title": "Basketball Game", "start": "November 8, 2018 7:30 PM", "end": "November 8, 2018 10:30 PM"}, {"title": "Local Pub with Friends", "start": "November 1, 2018 7:30 PM", "end": "November 1, 2018 11:00 PM"}, {"title": "Dentist Appointment", "start": "November 10, 2018 1:45 PM", "end": "November 10, 2018 2:30 PM"}, {"title": "Free Donuts", "start": "November 9, 2018 3:00 PM", "end": "November 9, 2018 4:00 PM"}, {"title": "TV Show Marathon", "start": "November 9, 2018 4:30 PM", "end": "November 9, 2018 9:00 PM"}, {"title": "Lunch with Friends", "start": "November 8, 2018 11:30 AM", "end": "November 8, 2018 1:00 PM"}, {"title": "SF Coffee Festival","start": "November 6, 2018 6:00 PM","end": "November 6, 2018 9:00 PM"}, {"title": "Beer with Friends", "start": "November 9, 2018 8:00 PM", "end": "November 9, 2018 9:30 PM"}, {"title": "Yoga", "start": "November 1, 2018 6:00 PM", "end": "November 1, 2018 7:30 PM"}, {"title": "Rock Concert", "start": "November 7, 2018 6:30 PM", "end": "November 7, 2018 11:00 PM"}, {"title": "Lunch Meeting", "start": "November 9, 2018 12:30 PM", "end": "November 9, 2018 2:30 PM"}, {"title": "Bicycling with Friends", "start": "November 1, 2018 6:00 AM", "end": "November 1, 2018 9:30 AM"}, {"title": "Birthday Party", "start": "November 10, 2018 12:30 PM", "end": "November 10, 2018 8:30 PM"}, {"title": "Football Tailgate with John", "start": "November 3, 2018 6:00 PM", "end": "November 3, 2018 10:00 PM"}]
}
"""

-> then you need to create a codable class or a struct

struct Main: Codable {
    var data: [MYStruct]?
}

// MARK: - Datum
struct MYStruct: Codable {
    var title, start, end: String?
}

-> if the key "start": "November 3, 2018 6:00 PM" here "start" deffers you can use codingKey like this

struct MYStruct: Codable {
    var title, start, end: String?
    enum CodingKeys: String, CodingKey {
        case title = "title" // key name as string 
        case startDate = "start"
        case endDate = "end"
}

-> then Create a JSON decoder to decode jsonData

override func viewDidLoad() {
    super.viewDidLoad()

    let jsonData = Data(json.utf8)
    let decoder = JSONDecoder()
    let loadedData = try? decoder.decode(Main.self, from: jsonData) // 1st parameter object name 2nd parameter JsonData

    print(loadedData?.data?[0].title)
    
} 

-> keep in mind you json is always proper strat and ends with {}

-> always should be in key pair value

-> Output

Optional("Evening Picnic")