2
\$\begingroup\$

This is the Swift code from one of the view controllers in my app that calculates a one rep maximum weight a person can theoretically lift. This particular view controller presents a list in a tableView of all types of lifts a user can choose from to set as a default preference. When the user selects a lift name, the refresh() method is called which places a checkmark on that selected item. The selected lift name is then set as the new default value and will be included in the lift log if the user chooses to save it (in another view).

I'm a beginner programmer and new to Swift. This code works, but there are a couple of areas that just don't feel right:

  • the refresh() function: I struggled to figure out a way to set the upper range in the for loop. Below is the only thing I got to work.
  • I didn't explicitly adopt the UITableViewDataSource or UITableViewDelegate protocols which I have to have specified, yet it still works. I haven't been able to figure out why.

I'd appreciate any critiques and suggestions anybody has for me.

import UIKit
import CoreData

class LiftsTableViewController: UITableViewController, NSFetchedResultsControllerDelegate {
  var coreDataStack = CoreDataStack()
  var lifts = [Lift]()
  var preferredLift: String?

  func refresh() {
    let allLifts = self.fetchedResultsController
    for index in 0 ... (allLifts.fetchedObjects?.count ?? 0 ) {
      let indexPath = NSIndexPath(forItem: index, inSection: 0)
      if let cell = tableView.cellForRowAtIndexPath(indexPath) {
        let currentLift = cell.textLabel?.text
        cell.accessoryType = preferredLift == currentLift ? .Checkmark : .None
      }
    }
  }

  override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
    refresh()
  }

  override func viewDidLoad() {
    super.viewDidLoad()

    let userDefaults = NSUserDefaults.standardUserDefaults()
    let lift = userDefaults.objectForKey("preferredLift") as! String?
    if(lift != nil) {
      self.preferredLift = lift
    }
  }

  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
  }

  // MARK: - Table view data source

  lazy var fetchedResultsController : NSFetchedResultsController = {
    let request = NSFetchRequest()
    let entity = NSEntityDescription.entityForName("Lift",
               inManagedObjectContext:self.coreDataStack.managedObjectContext)
    request.entity = entity
    request.fetchBatchSize = 20
    let sortDescriptor = NSSortDescriptor(key:"liftName", ascending:true)
    request.sortDescriptors = [sortDescriptor]
    let afrc = NSFetchedResultsController(fetchRequest:request,
               managedObjectContext:self.coreDataStack.managedObjectContext,
               sectionNameKeyPath:nil, cacheName:"Lifts")
    afrc.delegate = self
    do {
      try afrc.performFetch()
    } catch {
      print("Unresolved error \(error)")
      fatalError("Aborting with unresolved error")
    }
    return afrc
  }()

  override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return 1
  }

  override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    let sectionInfo = self.fetchedResultsController.sections![section]
    return sectionInfo.numberOfObjects
  }

  override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("liftsCell", forIndexPath: indexPath)
    let object = self.fetchedResultsController.objectAtIndexPath(indexPath) as! Lift

    cell.textLabel?.text = object.liftName

    return cell
  }

  override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    tableView.deselectRowAtIndexPath(indexPath, animated: true)
    guard let object = self.fetchedResultsController.objectAtIndexPath(indexPath) as? Lift else {
      return
    }
    let selectedLift = object.liftName as String

    savePreferences(selectedLift)

    refresh()
  }

  func savePreferences(lift: AnyObject) {
    let userDefaults = NSUserDefaults.standardUserDefaults()
    userDefaults.setObject(lift, forKey: "preferredLift")

    preferredLift = lift as? String
  }
}
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

I think you will find that the above code, in fact, does not work if you have enough lifts in the table view such that the selected lift must get reloaded. The reason I think this is because you don't selected lift's accessoryType in the cellForRowAtIndexPath method. Also, I think your refresh method is doing way more work than it needs to. All it needs to do is note which index path the current selected lift is at, change the currentLift to the new value, and then reload just the old selected lift cell and the new selected lift cell. There is no reason to loop through anything.

As for why your methods are getting called even though you have explicitly declared your class as a delegate/dataSource... That's a hold-over from Objective-C where such things weren't necessary.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.