Apologies
You must be signed in to watch this lesson.
Protocol Oriented Programming Refactor
Intermediate Training Core Data
Having completed almost all the creation, reading, updating, and deleting of our company objects, we're in position to apply some refactoring to our code. We'll have a look at how we can apply Protocol Oriented Programming (POP) to separate our classes into files containing only protocol specific methods. This is a practice that is extremely useful for keeping your code clean and easy for everyone on your team to read.

Comments (16)
Alpensol
6 years ago
Hey Brian, A question that is a bit off topic, are there libraries in Objective C that are not available in Swift. If so does this mean that a Swift developer should have at least a basic understanding of objective C in order to become a better iOS developer overall? Would you recommend learning Objective C basics or do you think these skills are not required? I have been looking at libraries like AudioToolBox and they are in objective C, cant really understand them at the moment but kind find a way around it either. Thanks
Brian Voong
6 years ago
camman564
6 years ago
Hello Brian, Having a little trouble with the refactoring of the handleReset function. I started by moving the function into CoreDataManager because most of the code dealt with removing items from CoreData. Got hit with a lot of errors, so I pulled all the code that had to do with removing rows and data from the array back into the controller as a function called deleteAllCompanies(). I couldn't really figure out how to call that function from CoreDataManager, because I couldn't come up with a way to access the instance of CompaniesController. I left it blank to try and follow the flow with breakpoints but got a crash, I'm assuming, because I changed the #selector to CoreDataManager.handleReset I get this error: Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Intermediate_Training.CompaniesController handleReset]: unrecognized selector sent to instance 0x7fd978e05f00' Any hints? Thanks
Brian Voong
6 years ago
Esat Kemal Ekren
6 years ago
I couldn't figure it out to put in CoreDataManager...
Esat Kemal Ekren
6 years ago
for handle reset function
Esat Kemal Ekren
6 years ago
I try to do something like this in coreDatamanager but it didn't worked func handleReset(companiese: [Company]) -> [Company] { var companies = companiese let context = persistentContainer.viewContext let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: Company.fetchRequest()) do { try context.execute(batchDeleteRequest) var indexPathRemove = [IndexPath]() for (index, _) in companies.enumerated() { let indexPath = IndexPath(row: index, section: 0) indexPathRemove.append(indexPath) } companies.removeAll() return [] }catch let delErr { print("Error in deleting all data:", delErr) return [] } }
Brian Voong
6 years ago
Redoine Aito
6 years ago
CoreDataManager.shared.deleteAll(companies: companies) { (indexPathsToRemove) in companies.removeAll() tableView.deleteRows(at: indexPathsToRemove, with: .left) }
StanyMiles
6 years ago
This is how we're doing it... =) //CoreDataManager func deleteAllData() -> Bool { let context = persistentContainer.viewContext let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: Company.fetchRequest()) do { try context.execute(batchDeleteRequest) return true } catch { print("Failed to delete objects from Core Data:", error.localizedDescription) return false } } //CompaniesController @objc private func handleReset() { if CoreDataManager.shared.deleteAllData() { var indexPathsToRemove = [IndexPath]() for (index, _) in companies.enumerated() { let indexPath = IndexPath(row: index, section: 0) indexPathsToRemove.append(indexPath) } companies.removeAll() tableView.deleteRows(at: indexPathsToRemove, with: .left) } }
lewisle
6 years ago
CoreDataManager’s responsibility should only involve CoreData related tasks and should not have any concerns with updating the UI, which is the responsibility of the view controllers. This is how I would refactor the `handleReset` function. I think using completion block is a pretty decent choice. ``` // CoreDataManager.swift func resetCompanies(completion: (_ error: String?) -> ()) { let context = persistentContainer.viewContext let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: Company.fetchRequest()) do { try context.execute(batchDeleteRequest) completion(nil) } catch let delErr { let error = "Failed to batch delete companies: \(delErr)" completion(error) } } // CompaniesController.swift @objc func handleReset() { CoreDataManager.shared.resetCompanies { (error) in if error != nil { // maybe show a user-frendly messsage here... } else { var indexPathsToRemove = [IndexPath]() for (index, _) in companies.enumerated() { indexPathsToRemove.append(IndexPath(row: index, section: 0)) } companies.removeAll() tableView.deleteRows(at: indexPathsToRemove, with: .left) } } } ```
Daniel Peach
6 years ago
Quick question: How does Company have a .fetchRequest()? When I go to the definition of Company its just the empty looking file that has this: import Foundation import CoreData @objc(Company) public class Company: NSManagedObject { } Is it inheriting the fetchRequest() from NSManagedObject?
Brian Voong
6 years ago
mohitnandwani
6 years ago
Resetting companies: in CoreDataManager file: func resetCompanies(completion: () -> ()) { let context = CoreDataManager.shared.persistentContainer.viewContext let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: Company.fetchRequest()) do { try context.execute(batchDeleteRequest) completion() } catch { print(error.localizedDescription) } } In CompaniesController File: @objc private func handleReset() { CoreDataManager.shared.resetCompanies { var indexPathToRemove = [IndexPath]() for (index, _) in companies.enumerated() { let indexPath = IndexPath(row: index, section: 0) indexPathToRemove.append(indexPath) } companies.removeAll() tableView.deleteRows(at: indexPathToRemove, with: .left) } }
RichyCode
6 years ago
solution if your struggling....isnt that hard as brian has kindly gone through this with us....we re just refactoring with a completion block :- CoreDataManager...... func resetCompanies(completion: () -> ()) { let context = CoreDataManager.shared.persistantContainer.viewContext let deleteBatchRequest = NSBatchDeleteRequest(fetchRequest: NewCompany.fetchRequest()) do { try context.execute(deleteBatchRequest) completion() } catch let err { print("unable to delete batch of companies \(err)") } } and in your view controller :- @objc private func handleResetButton() { CoreDataManager.shared.resetCompanies { <---the function name you refactored to var indexPathToRemove = [IndexPath]() for(index, _) in companies.enumerated() { let indexPath = IndexPath(row: index, section: 0) indexPathToRemove.append(indexPath) companies.removeAll() } tableView.deleteRows(at: indexPathToRemove, with: .left) } }
Christophe Bugnon
6 years ago
Thanks for this reply, I just really understand with your comment how really works closure. Thanks dude ! :D
forsuresh
6 years ago
class CompaniesController: UITableViewController builds for me. However, class CompaniesController{....} & extension CompaniesController:UITableViewController doesn't compile. I thought it would be a good idea to add the UITableView to the same file with all the UITableview extensions. Is there something particular with UITableView? Thank you so much for uploading such an informative video series.
justinjaster
6 years ago
Hey Brian, Thanks for the great course. I'm loving the refactoring aspect of things to get our project organized and to separate concerns appropriately. How can we move all interface elements that were created in CreateCompanyController to another file so it cleans up the controller a bit? It's about 40 lines of code, but I've had larger controllers that had maybe 100+ lines of closures that were creating all of my interface elements. I've solved this in a past project by subclassing UIView and grouping related elements into that particular class, and then creating an instance in the controller in which I want it to display, but I still have to configure AutoLayout for that view which does contain less code, I just don't like putting any view logic (especially how it's displayed) in my controller. What do you think? Justin
Brian Voong
6 years ago
justinjaster
6 years ago
Sweet, sounds good. Moving away from storyboards has been the best decision I've ever made, so I appreciate the work you're doing. It's allowed me to grow as a developer and have much more flexibility and less ambiguity when it comes to laying out my interface. I love your anchor extension idea, and I added to it a bit, and I'd like to know what you think. It's essentially the same as yours, except I'm using a tuple where we can access the anchor and constant if the developer wanted to use a custom constant: public func anchor(top: (anchor: NSLayoutYAxisAnchor, constant: CGFloat)? = nil /* left, bottom, right */) { /* ... */ } It's too bad tuples don't support default arguments or it'd look super clean when calling anchor on a subclass of UIView.
magic
6 years ago
Dude you are a LEGEND!
Tube
6 years ago
I enjoyed this lesson; but, I was surprised to find that it had nothing to do with POP. Using extensions just to hold bits of source code in various source files is not POP. The only use of extensions related to POP--that I know of--is to provide default protocol implementations. POP is about using protocols instead of multiple inheritance (as in C++).
cicihuang
5 years ago
Hi Brain, In video 15, you mentioned that in ViewDidLoad(), it is special because it will automatically reload at the end of the function. However, when I create a new Company, the tableView is not reloaded. I wonder if you can explain to me why.
cicihuang
5 years ago
Thank you!
Caroline Kuo
5 years ago
Question: Why you didn’t move the “: UITableView” from CompanyController over to the other file on extension line? Perhaps it doesn’t matter for the compiler?
Portocarrero Lopez Pedro
4 years ago
i did it and everything works normal :)
Portocarrero Lopez Pedro
4 years ago
i mean the methods "numberOfSections,numberOfItems, cellForRow" , extensions cant have variables.
Cinquain
4 years ago
This refactoring video was fire
HELP & SUPPORT