Post

Loading SampleData in CoreData

When I started developing my first app, I based a lot of what I was doing on what I learned from Paul Hudson’s 100 Days of SwiftUI and his Hacking With Swift+ Ultimate Portfolio app. For my sample data, I created a function that loops through and create some sample data. Unfortunately, because I need to have multiple nested loops, I ended up with a lot of generic entries, like “Prayer List 1”, “Prayer Subject 2”, etc. I know it is just sample data, but I wanted something that was a little more realistic, especially when I loaded it into my simulator.

I could have done it within my function, making it larger and more complicated, but instead I decided to look into what it would take to load my sample data from a JSON file. That way, I can quickly change the data I want to add without having to rewrite my function. It turns out , it is pretty easy.

The first thing that I did was create an extension for my Persistence Controller in a new file so that my sample data logic is self contained.

1
2
3
extension PersistenceController {

}

Next I created a json file to hold my prayer data. The structure starts with my prayer list, which contains prayer subjects, which contains individual prayers. I decided not to put all of the attributes in JSON. Some things can be auto-generated like dates.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[
    {
        "name": "Daily",
        "order": 0,
        "weekdays": [ 1, 2, 3, 4, 5, 6, 7 ],
        "prayerSubjects": [
            {
                "name": "Personal",
                "order": 0,
                "prayer": [
                    {
                        "name": "Discipline & Focus",
                        "order": 0
                    }
                ]
            },
            {
                "name": "Spouse",
                "order": 1,
                "prayer": []
            }
        ]
    }
]

Next, I needed to create the models for may prayer list to be imported to. Since these models specifically for my JSON, I just included them in the extension I created.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
extension PersistenceController {

    struct SamplePrayerList: Codable {
        let name: String
        let order: Int16
        let weekdays: [Int16]
        let prayerSubjects: [SamplePrayerSubject]
    }

    struct SamplePrayerSubject: Codable {
        let name: String
        let order: Int16
        let prayer: [SamplePrayer?]
    }

    struct SamplePrayer: Codable {
        let name: String
        let order: Int16
        let details: String?
    }
}

Finally, I created a function to load the data from JSON and then looped through it to add everything to CoreData.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
extension PersistenceController {
    func loadSampleData() {
        let context = container.viewContext
        guard let url = Bundle.main.url(forResource: "sampleData", withExtension: "json") else {
            print("JSON file not found")
            return
        }

        do {
            let data = try Data(contentsOf: url)
            let decoder = JSONDecoder()
            decoder.dateDecodingStrategy = .iso8601
            let samplePrayerLists = try decoder.decode([SamplePrayerList].self, from: data)

            for list in samplePrayerLists {
                let newList = PrayerList(context: context)
                newList.name = list.name
                newList.order = list.order
                newList.creationDate = Date().minus(7)
                for weekday in list.weekdays {
                    let weekdayItem = WeekdayItem(context: context)
                    weekdayItem.value = weekday
                    newList.addToWeekdays(weekdayItem)
                }
                for subject in list.prayerSubjects {
                    let subjectItem = PrayerSubject(context: context)
                    subjectItem.name = subject.name
                    subjectItem.order = subject.order
                    subjectItem.creationDate = Date().minus(7)
                    newList.addToSubjects(subjectItem)

                    for prayer in subject.prayer {
                        if let prayer = prayer {
                            let newPrayer = Prayer(context: context)
                            newPrayer.name = prayer.name
                            newPrayer.order = prayer.order
                            newPrayer.details = prayer.details
                            newPrayer.creationDate = Date().minus(7)
                            subjectItem.addToPrayers(newPrayer)
                        }
                    }
                }

            }

            try context.save()
        } catch {
            print("Failed to load sample data: \(error)")
        }
    }
    
    // Define a struct to match the JSON structure
    struct SamplePrayerList: Codable {
        let name: String
        let order: Int16
        let weekdays: [Int16]
        let prayerSubjects: [SamplePrayerSubject]
    }

    struct SamplePrayerSubject: Codable {
        let name: String
        let order: Int16
        let prayer: [SamplePrayer?]
    }

    struct SamplePrayer: Codable {
        let name: String
        let order: Int16
        let details: String?
    }
}


This way, no matter how much data I want to load in, my function stays the same. All I have to do is edit my JSON file to change the contents of my test database. It also makes it easier to have multiple datasets. All I need to do is change out the JSON file.