Editing Company Entities
Intermediate Training Core Data
Being able to edit objects is a big part of an application. Moreover, keeping your data consistent with all the changes happening throughout the lifecycle of your app is very important. In this lesson, we go ahead and implement an edit swipe action for our UITableView as well as edit the entity we've stored in Core Data. Finally we'll need to refresh our main list if edits have been successfully saved.

Comments (13)
Alpensol
5 years ago
Hey Brian, I have a question about the reloading of the cell after the editing, how does the array data update itself allowing with the new edited company? In didUpdateCompany methods you use the array that we constructed from the initial CD fetch, we never refetched the updated data that would contain the updated company. Great course by the way, everything else I looked at for Core Data before this course seemed so overcomplicated. Thanks
Brian Voong
5 years ago
Dejan Ribnikar
5 years ago
Hi Brian! Great course, but I ran into a problem. When I'm updating the company name, values get stored properly and the edited name displays when I relaunch the app, but for some reason, didUpdateCompanies never gets executed. The breakpoint activates where it's calling the delegate, but the reloading of cells never actually happens. I downloaded your project and tripple-checked everything, but it seems to be identical... Please help
Dejan Ribnikar
5 years ago
I'm happy to say I solved the issue. Forgot to set editCompanyController.delegate = self. Everything is working now. Keep up the good work, you have the best tutorials by far! :D
Daniel Peach
5 years ago
Brian, Great Video once again! I have a question about this as well. It has to do with the persisting aspect of this. With this code: let context = CoreDataManager.shared.persistentContainer.viewContext company?.name = nameTextField.text do { try context.save() dismiss(animated: true) { self.delegate?.didEditCompany(company: self.company!) } } catch let updateErr { fatalError("Failed to update company with Error: \(updateErr)") } } I am confused how the context knows to persist the new name we set. It makes sense that we are setting the name of the company to the textField text, but then how does the context know to get that new name and store it in CD? To me it seems we are getting the context, updating the company name in the local companies array, and then saving the context without having done anything to it. How does the context know to update when it seems we are only updating the local companies array? It works, but I don't know how or why it is working. I don't see anywhere that we instruct the context itself to update. If anyone else can answer this either, please do. Thanks!
Daniel Peach
5 years ago
Ok the only way I can see that this is working is because companies is in fact NOT a local array. And in fetchCompanies() when we say: self.companies = companies in which we got companies from: let companies = try context.fetch(NSFetchRequest<Company>(entityName: "Company")) that we are only setting a reference to the array of companies in the shared persisted context. And this is why when we update something in self.companies, that the array of companies in context is automatically getting updated? This is the only explanation I can think of that makes sense. If this is it, please answer anyway just so I know, and if not PLEASE explain what is really going on, because I want to understand it. Really though, awesome explanations of CoreData, no where else have I found such an easy explanation for it! Thanks Brian!
Brian Voong
5 years ago
mcordero
5 years ago
Brian could you please post the source code for each lesson? I keep getting errors that seem to be un-debuggable.
mcordero
5 years ago
the error is: exception 'NSInvalidArgumentException', reason: '-[Company setName:]: unrecognized selector sent to instance it happens in the saveCompanyChanges() at : company?.name = nameTextField.text
Brian Voong
5 years ago
mcordero
5 years ago
Ok thanks! I was able to fix it. :-) Is there a way I could delete the comments on this website?
Ivan Amidžić
5 years ago
Hey Brian, I finished the course and now im trying to "upgrade" application a bit to learn more. Currently im trying to add edit button for employees but im having hard time how to set section in IndexPath properly. Current code allows me to edit employee in same section but crashes without guard statement when trying to change section of employee. func didEditEmployee(employee: Employee) { guard let section = employeeTypes.index(of: employee.type!) else { return } guard let row = allEmployees[section].index(of: employee) else { return } let reloadIndexPath = IndexPath(row: row, section: section) tableView.reloadRows(at: [reloadIndexPath], with: .middle)
Prabhdeep Singh Randhawa
5 years ago
really enjoyed this one Brian. what can we do Brian to make sure the value is inserted once only.so if we already added Google in list it should not be repeated again or should replace the old google element in list. Thanks in advance.
Brian Voong
5 years ago
albaqawi
5 years ago
This is an awesome and loaded class that contains all the CRUD / sending back data through delegates in 1 excellent video for iOS developers. Thank you Brain!
Daniel Peach
5 years ago
Brian, while trying to do something extra, I ran into something odd. In my view will appear function, anything after a guard let statement won't execute. Here is what I have: var c2: Company? override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) navigationItem.title = "Before Guard" print("before guard print") guard let editCompany = c2 else { return } if let editCompany = c2 { print("yes") } else { print("no") } print("After guard print") navigationItem.title = "After Guard" } What I get is a print that says "before guard print" and the title in the nav is set to Before Guard. Nothing after the guard let gets executed. why is this? Thanks!
Brian Voong
5 years ago
Daniel Peach
5 years ago
Why does that stop unrelated code from running? I would think the guard let would just have to do with unwrapping c2, and even if the optional is nil, the random pront(“After guard”) that is unrelated would still execute. Why does this not happen?
Brian Voong
5 years ago
Daniel Peach
5 years ago
Oh! Ok I did not realize it returned out of the statement. Thanks so much Brian! Hey will you ever be doing an iOS game tutorial? I’d be interested in that. Or even just animations. Something besides views and controls. Custom graphics and animations would be cool. If you are not planning on doing that, where should I look to find such things? I don’t even know what to google.
bfriendjr
5 years ago
Brian, I am building on what I learned in the course and hit another road block and I think its due to another rookie mistake. I have three Core Data Entities named "CircuitSub", "Item", and "ItemSubCircuit". There is a one to one inverse relationship named "toCircuitSub" between "Item" and "ItemSubCircuit". I am successfully adding entries into all three Core Data Entities and displaying them within their specific VC within tableview cells that have swipe to "Edit" & "Delete" functionality. Within the CreateItem VC I have a UIPickerView that is being populated via the "CircuitSub" entity and displaying the names of all the Sub Circuits. I am also adding an "Unassigned" selection option at row "0". When I save the new item, it successfully writes "Unassigned" or the selected Sub Circuit name to "Item.toCircuitSub" name attribute as expected. The problem is when I select the swipe-able cell Edit action, I can not get the UIPickerview to display the "selected" Sub Circuit that was saved to the "Item.toCircuitSub" name attribute. I am 100% positive that there is a value for that attribute because I have a text field that is being successfully populated to verify that. If I comment out the "if let loadedSubCircuit" code, the info from the selected cell info loads just fine in the form and the UIPickerView displays all the Sub Circuit names as expected but is focused on the row zero "Unassigned" title. If I uncomment the "if let loadedSubCircuit" code, it crashes with an "Fatal error: Index out of range" on the "let s = circuitsSub[index]" line. I have literally worked on this issue for two days now and I can not put my finger on it. In addition to that issue, I would also like to sort (A>Z) the Sub Circuits within the UIPickerView while leaving the "Unassigned" at row zero. Do you have any pointers for that as well. //====================================================================== //MARK: VARIABLES //====================================================================== var delegateItems: CreateItemControllerDelegate? var itemSubCircuit: ItemSubCircuit? var itemElecSpecs: ItemElecSpecs? var circuitsSub = [CircuitSub]() //Empty Array var item: Item? { didSet { nameTextField.text = item?.name if let imageData = item?.image { itemImageView.image = UIImage(data: imageData) setupCircularImageSytle() } circuitSubNameTextField.text = item?.toItemSubCircuit?.name // START PROBLEM AREA if let loadedSubCircuit = item?.toItemSubCircuit?.name { var index = 0 repeat { let s = circuitsSub[index] //GIVES ERROR: Thread 1: Fatal error: Index out of range if s.name == loadedSubCircuit { circuitSubPicker.selectRow(index, inComponent: 0, animated: false) break } index += 1 } while (index < circuitsSub.count) } // END PROBLEM AREA } } //====================================================================== //====================================================================== //MARK: VIEW CONTROLLER LIFECYCLES //====================================================================== override func viewDidLoad() { super.viewDidLoad() setupUI() setupCancelButton() self.circuitsSub = CoreDataManager.shared.fetchCircuitsSub() self.circuitSubPicker.dataSource = self; self.circuitSubPicker.delegate = self; //circuitSubNameTextField.text = unassignedSubCircuitValue navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Save", style: .plain, target: self, action: #selector(handleSave)) view.backgroundColor = UIColor.colorThemeDkBlue } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) navigationItem.title = item == nil ? "Create Item" : "Edit Item" } //====================================================================== //====================================================================== //MARK: UIPICKERVIEW //====================================================================== func numberOfComponents(in pickerView: UIPickerView) -> Int { return 1 } func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { if pickerView == circuitSubPicker { return circuitsSub.count + 1 } return 0 } func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { if pickerView == circuitSubPicker { return row == 0 ? "Unassigned" : circuitsSub[row - 1].name } return "" } func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { if pickerView == circuitSubPicker { if row == 0{ print("Unassigned") circuitSubNameTextField.text = "Unassigned" } else { print(circuitsSub[row - 1].name ?? "") circuitSubNameTextField.text = circuitsSub[row - 1].name } } } //======================================================================
Iven Prillwitz
5 years ago
- unwrap all optionals safely - do UI changes on the main thread ... from the IOS dev bible
JulienR
4 years ago
Great job Brian (as usual) ! I have one question : in my project, I have several ViewControllers for "createCompanyController" embed in a UITabBarController : I create/edit some "companies" in 4 steps, so I have 4 VC. For create, no problem : I just present my tabBar. But for editing, I don't see how I can target a specific row in an embed vc in a tabBar... I tried by define my let editCompanyController like this : let editCompanyController = TapBar.viewControllers?[0] as! CreateCompanyStep1VC but it crashes... with a print func, it results that I have my 4 VC in my tabBar array of VC, it doesn't recognize them as VC but as Optional([<UINavigationController: 0x7fc29d02f000>.... If you have any idea, it would help me to solve my problem...
JulienR
4 years ago
Nom, I'm able to list my VC array and it recognizes it in my print func as VC : My views <AppName. CreateCompanyStep1VC: 0x7fb2eac2ff80> etc... let TabBar = MyTabBarController() let editCompanyController = TabBar.myVCtoShow[0] as! CreateCompanyStep1VC editCompanyController.company = companies[indexPath.item] let navController = UINavigationController(rootViewController: editCompanyController) This code only show my Destination VC with the nameTextField.text = company?.name, it doesn't display my tabBar.... It seems that i can not present both editing view and TabBar.
Brian Voong
4 years ago
JulienR
4 years ago
I'll think about it, thanks for your answer.
JulienR
4 years ago
f... mistake, the answer was under my noze : I just spent a few hours looking for ideas through StackOverflow etc... I was just presenting the wrong UInavigationController :( In short : Nevermind....
Bugger
4 years ago
Hi Brian, Thank you for a super tutorial. It's amazing. I have a problem after I tried to add more attribute in Company Entities. There was no error after build but the App can't open. There were a log of messages on output. Please suggest. Thanks.
Cinquain
4 years ago
Good stuff good stuff
adrapp
3 years ago
What about the handling of deleting Employees from the tableView that is grouped by Sections? Running into the "Invalid # of sections" crash once self.tableView.deleteRows is called.
iosmkarim
3 years ago
audio started problem after 10 minutes of that video
HELP & SUPPORT