I’ve been programming a test I’ve gotta do. It’s basically an app written in Swift 4 which allows the users just to add their friends with their name, last name and birthday. This data get stored through CoreData.
The specification asks for the app to allow adding, edit and deleting friends from the list (hence from the supporting CoreData DB). I know some swift but I’d love the code to be squeaky clean, with good practices, and syntactic sugar.
I’d love to hear comments, suggestions and remarks on how to improve the code, make it cleaner add some best practices, etc. I’ve been checking a couple of best practices manuals such as the RayWanderlich just to name one. More cool best practices manuals are welcomed!
GitHub
The most important classes are:
DatabaseServices.swift
private let _sharedInstance = DatabaseServices() class DatabaseServices { open class var sharedInstance: DatabaseServices { return _sharedInstance } let appDelegate = UIApplication.shared.delegate as! AppDelegate func saveUser(userName : String, userLastName : String, userBirthday : Date) -> CDUser{ let context = appDelegate.persistentContainer.viewContext let entity = NSEntityDescription.entity(forEntityName: Constants.CDUser, in: context) let newUser = CDUser.init(entity: entity!, insertInto: context) newUser.name = userName newUser.lastname = userLastName newUser.birthday = userBirthday do { try context.save() } catch { print("Failed saving") } return newUser } func getCDUsers()-> [CDUser]{ let context = appDelegate.persistentContainer.viewContext let request = NSFetchRequest<NSFetchRequestResult>(entityName: Constants.CDUser) request.returnsObjectsAsFaults = false do { let result = try context.fetch(request) return (result as? [CDUser])! }catch{ print("Error retrieving users") } return [] } func updateUser(_ user : CDUser){ let context = appDelegate.persistentContainer.viewContext do { try context.save() } catch { print("Updating object failed..!") } } func deleteUser(_ user : CDUser){ let context = appDelegate.persistentContainer.viewContext context.delete(user) do { try context.save() } catch { print("Deletion failed!") } } func didAddNewUser(){ let nc = NotificationCenter.default nc.post(name:Notification.Name(rawValue:"didAddNewUser"), object: nil, userInfo: ["message":"newData!", "date":Date()]) } }
ListViewController
class ListViewController: UITableViewController,ListViewProtocol{ var users = [CDUser]() var selectedUser : CDUser? var selectedIdx = 0 override func viewDidLoad() { super.viewDidLoad() setupUI() loadData() configureNotifications() } func setupUI(){ self.tableView.register(UINib(nibName: Constants.userCell, bundle: nil), forCellReuseIdentifier:Constants.userCell) navigationItem.rightBarButtonItem = UIBarButtonItem(title: "+", style: .plain, target: self, action: #selector(didPressAddNewRecord)) title = "Users list" } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } func loadData(){ users = DatabaseServices.sharedInstance.getCDUsers() } @objc func didPressAddNewRecord(){ performSegue(withIdentifier:Constants.segueShowDetail, sender: self) } func configureNotifications(){ let nc = NotificationCenter.default nc.addObserver(forName:Notification.Name(rawValue:"didAddNewUser"), object:nil, queue:nil) { notification in self.tableView.reloadData() } } // Mark: - TableView Datasource & Delegate func numberOfSectionsInTableView(tableView: UITableView) -> Int { return 1 } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return users.count } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell : UserCell = self.tableView.dequeueReusableCell(withIdentifier: Constants.userCell) as! UserCell // FIX: Check bounds & existence first let user = users[indexPath.row] cell.configureCell(user) return cell } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { selectedUser = users[indexPath.row] selectedIdx = indexPath.row performSegue(withIdentifier: Constants.segueShowDetail, sender: self) } override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { if editingStyle == .delete { deleteUser(users[indexPath.row]) users.remove(at: indexPath.row) tableView.deleteRows(at: [indexPath], with: .fade) } } override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if (segue.identifier == Constants.segueShowDetail) { let viewController:DetalleViewController = segue.destination as! DetalleViewController viewController.delegate = self if let selectedUser = selectedUser{ viewController.selectedUser = selectedUser } } } func deleteUser(_ usr : CDUser){ DatabaseServices.sharedInstance.deleteUser(usr) } // MARK : ListViewDelegate func didEditSelectedUser(_ user : CDUser){ users[selectedIdx] = user tableView.reloadData() } func didAddNewUser(_ user : CDUser){ users.append(user) tableView.reloadData() } }
DetailViewController
class DetalleViewController: UIViewController { var mode = EditionMode.add var selectedUser : CDUser?{ didSet{ self.mode = EditionMode.edit } } var delegate : ListViewProtocol? = nil var userSelectedDate = Date() @IBOutlet weak var lblName: UITextField! @IBOutlet weak var lblLastname: UITextField! @IBOutlet weak var lblBirthday: UITextField! override func viewDidLoad() { super.viewDidLoad() navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Save", style: .plain, target: self, action: #selector(didPressSave)) loadUser() setupUI() } func setupUI(){ let datePickerView = UIDatePicker() datePickerView.datePickerMode = .date lblBirthday.inputView = datePickerView datePickerView.addTarget(self, action: #selector(handleDatePicker(sender:)), for: .valueChanged) if mode == .add{ title = "Adding" lblName.text = "" lblLastname.text = "" }else{ title = "Edit" } } @objc func handleDatePicker(sender: UIDatePicker) { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "dd MMM yyyy" lblBirthday.text = dateFormatter.string(from: sender.date) userSelectedDate = sender.date } @objc func didPressSave(){ if mode == .add{ let newUser = DatabaseServices.sharedInstance.saveUser(userName: lblName.text!, userLastName: lblLastname.text!, userBirthday: userSelectedDate) delegate?.didAddNewUser(newUser) }else{ selectedUser?.name = lblName.text! selectedUser?.lastname = lblLastname.text! selectedUser?.birthday = userSelectedDate delegate?.didEditSelectedUser(selectedUser!) DatabaseServices.sharedInstance.updateUser(selectedUser!) } self.navigationController?.popViewController(animated: true) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } func loadUser(){ if let user = selectedUser{ lblName.text = user.name lblLastname.text = user.lastname! lblBirthday.text = Utils.convertDateToString(user.birthday!) userSelectedDate = user.birthday! }else{ lblBirthday.text = Utils.convertDateToString(userSelectedDate) } } }