I've got several UIViewController
s that are trying to do too much so I'm trying to learn how to clean them up so that they only have code that does UI things (ultimately getting things to conform to MVVM).
I know my code needs a lot of improvement so I'm going to start small by focusing on one particular behavior to ask for help with which is saving the user's selection as a user default, but keeping the code that does this out of the UIViewController
.
In this example from my app, I have a UITableViewController
called FormulasTableViewController
which has a list of the formulas the app can use for calculations to choose from:
class FormulasTableViewController: UITableViewController {
let formulas: [CalculationFormula] = [
CalculationFormula.epley,
CalculationFormula.baechle,
CalculationFormula.brzychi,
CalculationFormula.lander,
CalculationFormula.lombardi,
CalculationFormula.mayhewEtAl,
CalculationFormula.oConnerEtAl]
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
refresh()
}
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return formulas.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("formulasCell", forIndexPath: indexPath)
let currentFormula = formulas[indexPath.row]
cell.textLabel?.text = currentFormula.rawValue
return cell
}
func refresh() {
let preferredFormula = UserDefaultsManager.sharedInstance.preferredFormula
for index in 0 ..< formulas.count {
let indexPath = NSIndexPath(forItem: index, inSection: 0)
let currentFormula = formulas[indexPath.row]
if let cell = tableView.cellForRowAtIndexPath(indexPath) {
cell.accessoryType = preferredFormula! == currentFormula ? .Checkmark : .None
}
}
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
UserDefaultsManager.sharedInstance.preferredFormula = formulas[indexPath.row]
refresh()
}
}
As you can see above in tableView(tableView:didSelectRowAtIndexPath:indexPath)
, when the user selects one, UserDefaultsManager.sharedInstance.preferredFormula = formulas[indexPath.row]
is executed.
class UserDefaultsManager {
static let sharedInstance = UserDefaultsManager()
// MARK: Preferred Formula
private var defaultsFormula: CalculationFormula?
var preferredFormula: CalculationFormula? {
get {
if self.defaultsFormula == nil {
let defaults = NSUserDefaults.standardUserDefaults()
if let defaultValue = defaults.objectForKey("preferredFormula") as? String {
self.defaultsFormula = CalculationFormula(rawValue: defaultValue)
} else {
self.defaultsFormula = CalculationFormula(rawValue: "Baechle")
}
}
return self.defaultsFormula
}
set {
self.defaultsFormula = newValue
let defaults = NSUserDefaults.standardUserDefaults()
if (self.defaultsFormula == nil) {
defaults.removeObjectForKey("preferredFormula")
} else {
defaults.setObject(self.defaultsFormula!.rawValue, forKey: "preferredFormula")
}
}
}
}
My thinking here is that because selection of a formula is happening in a table which is controlled by UIFormulasTableViewController
, UserDefaultsManager.sharedInstance.preferredFormula = formulas[indexPath.row]
shouldn't be here.
I think I want to use delegation so that my UserDefaultsManager
class will handle the user selection of a table row. But my UserDefaultsManager
class is handling a lot of different default settings so if I create a protocol on all the different UIViewController
s that are similar to my UIFormulasTableViewController
so UserDefaultsManager
can act as their delegate, it's going to conform to a long list of these protocols and that seems silly - but I'm a beginner, so maybe it's not.
To remove this code from the UIViewController
would it be reasonable to take this delegation approach?