With all of our JSON objects downloaded into our Service function, we're now ready to convert all of this data into Core Data entities. We start off by inserting all of our companies into Core Data using a private context, that has our main queue context as the parent. It's very important that you set this relationship up correctly, otherwise the saving of a private context will not push the changes to the UI. Finally we create our employees along with their information in Core Data. We wrap it all up by enabling the download functionality to fire when we drag our table view downwards.

Comments (18)
StanyMiles
5 years ago
Hey. Cool tutorial. I've almost finished) I have this little error.. override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { let label = IndentedLabel() label.text = fetchedResultsController.sectionIndexTitles[section] // here it crushes by: Index out of range label.backgroundColor = UIColor.lightBlue return label } it crushes when trying to get name for header. Do you know why it could be? Thanx
Brian Voong
5 years ago
lanchuanqi
5 years ago
Hi Brain, I have the same crash. So the first time I run the program it was fine and then when I try to run again(download all the companies for the second time) it crashed here with index out of range. Any ideas?
scottschmidt
5 years ago
Hi Brian. Have you tried running this in the iPhone 10 simulator? Interesting. JSON never loads on view. Very, very slow. Runs fine in other simulators.
Brian Voong
5 years ago
shender ramos
5 years ago
hey Brian can you add a video on avoiding data duplication when downloading data from the server or core data is smart enough to recognize the data id
seekerlk
4 years ago
No you have to write the logic for it manually. Core Data will not handle it for you.
shender ramos
4 years ago
Yea i figure thanks for answering
Saska Radosavljevic
4 years ago
Can you share the logic please? :)
iHobbit
5 years ago
Are you planning to do a future lesson where you illustrate the right way to use the image URLs? Does it end up making sense to create a separate private context to go and try to download the images?
Brian Voong
5 years ago
lythonik
5 years ago
Hey Brian, I cannot fix the employee bug . The apple Employees don't show up in my UI. even after adding the line " employee.company = company" they are still not showing up Can you take a look at my Service codes here ? //———————————Service.swift —————————// import Foundation import CoreData struct Service { //Turn this Service into a Singleton static let shared = Service() // the Json Data let urlString = "https://api.letsbuildthatapp.com/intermediate_training/companies" func downloadCompaniesFromServer(){ print("Attempting to download companies...") guard let url = URL(string: urlString) else { return } //Let's do the download here to grab the json URLSession.shared.dataTask(with: url) { (data, resp, err) in print("Finished Downloaing the JSON") if let err = err { print("Failed to download companies:", err) return } //Print out what we are downloading guard let data = data else { return } //----The Parsing with Decodeable let jsonDecoder = JSONDecoder() do { let jsonCompanies = try jsonDecoder.decode([JSONCompany].self, from: data) //Context on the backGround Thread let privateContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) //From previous Parent-child context privateContext.parent = CoreDataManager.shared.persistentContainer.viewContext jsonCompanies.forEach({ (jsonCompany) in print(jsonCompany.name) //To save our JSON Object into CoreData Object let company = CompanyEntity(context: privateContext) //lets use the properties from company to our json ones company.name = jsonCompany.name //convert the JSON date from string to Data object let dateFormatter = DateFormatter() dateFormatter.dateFormat = "MM/dd/yyyy" let foundedDate = dateFormatter.date(from: jsonCompany.founded) company.founded = foundedDate //loop to print the employees for companies that have ones jsonCompany.employees?.forEach({ (jsonEmployee) in print(" \(jsonEmployee.name)") //Let's download the Json Employee object into CoreData let employee = Employee(context: privateContext) employee.name = jsonEmployee.name employee.type = jsonEmployee.type let employeeInformation = EmployeeInformation(context: privateContext) let birthdayDate = dateFormatter.date(from: jsonEmployee.birthday) employeeInformation.birthday = birthdayDate employee.employeeInformation = employeeInformation employee.company = company }) do { try privateContext.save() try privateContext.parent?.save() } catch let saveErr { print("Unable to save Json object data into CoreData", saveErr) } }) } catch let jsonDecodeErr { print("Failed to decode ", jsonDecodeErr) } }.resume() //import must call for the downloading to start } }// end Service struct //Company Struct with the fields interested of getting from the Json api //Important make it Decodable struct JSONCompany: Decodable { let name: String let founded: String var employees: [JSONEmployee]? } //Now let's defined the JSONEmployee object, make it also Decodable struct JSONEmployee: Decodable { let name: String let type: String let birthday: String } //———————CompaniesAutoUpdateController.swift——————// import UIKit import CoreData class CompaniesAutoUpdateController: UITableViewController, NSFetchedResultsControllerDelegate { …………………………………….. ……………………………………. ……………………………….. //-------To display the employees coming from JSON Api override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let employeesListController = EmployeesController() employeesListController.company = fetchResultsController.object(at: indexPath) navigationController?.pushViewController(employeesListController, animated: true) } }//end Class
jpickard
5 years ago
May want to add time zone to the mix for dates (timestamps) that include time. let dateFormatter = DateFormatter() dateFormatter.timeZone = TimeZone(abbreviation: "GMT") dateFormatter.dateFormat = "YYYY/MM/DD HH:mm:ss.SSS"
Haohong Zhao
4 years ago
Hi Brian, Do we need to execute "privateContext.parent.save()" on the main thread, since the parent of "privateContext" is a context on the main thread?
Brian Voong
4 years ago
reckk1
4 years ago
Hi Brian, Could you do a video that shows how to load the image on the ui derived from the JSON file?
zslavman
4 years ago
Hi Brian, thanks for your great work! The question is - how to download images from JSON and paste it into Core Data? You have images links in JSON, but you don't use it in all of this course.
Cinquain
4 years ago
Good stuff good stuff!
iOS-Dev
4 years ago
Hi Brian, I am deleting the companies from core data once i received data from API, then again storing latest API's data to core date, during this process my tableview is flickering, is there any way to delete the existing data and reload new data without flickering the UI.
Brian Voong
4 years ago
iOS-Dev
4 years ago
still same issue, even i removed NSFetchResultsController, i Service class i am deleting the data from core data, but when i reload the tableview with updated recored its flickering. override func viewDidLoad() { super.viewDidLoad() tableView.register(CompanyCell.self, forCellReuseIdentifier: cellId) self.companies = CoreDataManager.share.fetchCompanies() self.tableView.reloadData() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) Service.share.downloadCompaniesFromServer { (value) in if value == true { self.companies = CoreDataManager.share.fetchCompanies() DispatchQueue.main.async { self.tableView.reloadData() } }else { } } }
Jose Jonas Abesamis
4 years ago
cool thanks
Sebastien Menozzi
4 years ago
Salut Brian, I don't know if it's a good implementation or if it's too complicated: I would suggest for inserting from API to Core Data: fetching from the API via Decodable (this allows to have a flexible JSON result, for instance when dealing with pagination with has_next attribute), transforming Decodable model to JSON, then transforming the JSON to Managed Object(s) (Core Data entities) via a simple recursive parser to deal with relationship entities. We can then insert/update the Managed Object(s) to Core Data with relationships included. When we want to load the data from Core Data (when we're offline for instance), I retrieve the Managed Objects, then I transform these entities to Encodable (I actually use Codable), and finally, I use this codable objects for my view models and I update the list. I actually do the opposite of what I was doing to insert new data. I'm doing it that way because I need immutable objects for my view models, it's a requirement when using IGListKit (Pretty sure you already heard about it). Indeed, Managed Objects are mutable and it would be dirty to use them for my view models. I don't know if I'm clear, it's been only two months I code in Swift
Brian Voong
4 years ago
Sebastien Menozzi
4 years ago
Actually I have separate view models, they aren't codable objects, they are ListDiffable :) I use codable models as "a data object" to populate my view models. Basically I do for inserted new data : API => Codable => JSON Dictionary => NSManagedObject, and to fetch and display data locally : Core Data => NSManagedObject => JSON Dictionary => Codable => View Models. The main issue of this implementation is that I need to write the Codable model and the NsManagedObject Model, as they are pretty similar there are repetitions, and when I want to update a model, I have to do it in the Interface Builder, the Codable model and the NSManagedObject (and sometimes the view models too!)
Brian Voong
4 years ago
m.k.hasson97
3 years ago
Hey Brian, can you please make another video about how to replace (update) the data when that app reloads new JSON data, instead of duplicate the data. hopefully you see my comment. Thank's a lot
m.k.hasson97
3 years ago
No Reply?!
Sanook
3 years ago
Hey Brian, Just bought the Core Data class, super awesome! After months of study and reading (and not understanding) your videos put it all together in a totally understandable way. I am _finally_ starting to understand CoreData. Thanks! I have a quick questions about setting the employee birthdate. In the video (about 13:30 mark) you have the birthdate assigned as: ... 1 let employeeInformation = EmployeeInformation(context: privateContext) 2 let birthdayDate = dateFormatter.date(from: jsonEmployee.birthday) 3 employeeInformation.birthday = birthdayDate 4 5 employee.employeeInformation = employeeInformation ... I don't understand why it is done this way. Can't birthdayDate be assigned as: employee.employeeInformation?.birthday = birthdayDate by taking advantage of the relationship in the CoreData model? Second question: do you have any subscription service? Or multiple video discounts? I'm just a starving student here ?.
Mula SubbaReddy
3 years ago
Provide Coredata source code
mry
2 years ago
....
iosmkarim
2 years ago
Hey Brian, Can you please mention how to avoid duplicate data, every time I do pull to refresh it shows duplicate data in ui.
HELP & SUPPORT