Skip to main content
7 of 7
This question isn't even remotely about Objective-C.
nhgrif
  • 25.4k
  • 3
  • 64
  • 129

Rewrite code from Objective-C to conform with Swift power tools and concise style

I started a project with Objective-C and rewrote it with Swift. The project contains two UITableViewControllers: MasterViewController and DetailViewController. MasterViewController is linked to DetailViewController with a segue.

MasterViewController has two UITableViewCells. The cell with indexPath [0, 0] has a detailTextLabel that displays a String. Each time I click on this cell, I go to the DetailViewController which, in this case, displays a list of Strings. The cell with indexPath [0, 1] has a detailTextLabel that displays an Int. Each time I click on this cell, I go to the DetailViewController, which in this case, displays a list of Ints.

Furthermore, when I select a cell in DetailViewController, it displays a checkmark and updates the MasterViewController related cell which triggered the segue.

Here is a picture of the different scenes of the project:

enter image description here

And here is my code:

MasterViewController

class MasterViewController: UITableViewController {

    var myString = "Yellow"
    var myInt = 16
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "attributeChangeMethod:", name: "attributeChange", object: nil)
    }

    func attributeChangeMethod(notif: NSNotification) {
        if let passedString: AnyObject = notif.userInfo?["String"] {
            myString = passedString as String
            tableView.reloadRowsAtIndexPaths([NSIndexPath(forRow: 0, inSection: 0)], withRowAnimation: .Automatic)
        }
        if let passedInt: AnyObject = notif.userInfo?["Int"] {
            myInt = passedInt as Int
            tableView.reloadRowsAtIndexPaths([NSIndexPath(forRow: 1, inSection: 0)], withRowAnimation: .Automatic)
        }
    }

    deinit {
        NSNotificationCenter.defaultCenter().removeObserver(self, name: "attributeChange", object:nil)
    }

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

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 2
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell

        if indexPath.row == 0 {
            cell.textLabel?.text = "My string"
            cell.detailTextLabel?.text = myString
        }
        if indexPath.row == 1 {
            cell.textLabel?.text = "My int"
            cell.detailTextLabel?.text = "\(myInt)"
        }
        return cell
    }
    
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        let controller = segue.destinationViewController as DetailViewController
        
        if sender as? UITableViewCell == tableView.cellForRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0)) {
            controller.identifier = "String"
            controller.passedString = myString
        }
        if sender as? UITableViewCell == tableView.cellForRowAtIndexPath(NSIndexPath(forRow: 1, inSection: 0)) {
            controller.identifier = "Int"
            controller.passedInt = myInt
        }
    }

}

DetailViewController

class DetailViewController: UITableViewController {

    var identifier: String!
    var passedString: String!
    var passedInt: Int!
    var array: [Any]!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        if identifier == "String" {
            title = "Select My String"
            array = ["Yellow", "Green", "Blue", "Red"]
        }
        if identifier == "Int" {
            title = "Select My Int"
            array = [8, 16, 32, 64]
        }
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return array.count
    }
    
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
        
        if identifier == "String" {
            cell.textLabel?.text = array[indexPath.row] as? String
            cell.accessoryType = (array[indexPath.row] as String) == passedString ? .Checkmark : .None
        }
        if identifier == "Int" {
            cell.textLabel?.text = "\(array[indexPath.row] as Int)"
            cell.accessoryType = (array[indexPath.row] as Int) == passedInt ? .Checkmark : .None
        }
        return cell
    }
    
    override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        for cell in tableView.visibleCells() as [UITableViewCell] {
            cell.accessoryType = .None
        }
        
        tableView.deselectRowAtIndexPath(indexPath, animated: true)
        let cell = tableView.cellForRowAtIndexPath(indexPath)
        cell!.accessoryType = .Checkmark
        
        //Return the new value back to MasterViewController
        var dict: [String : AnyObject]!
        if identifier == "String" {
            passedString = array[indexPath.row] as? String
            dict = ["String" : array[indexPath.row] as String]
        }
        if identifier == "Int" {
            passedInt = array[indexPath.row] as? Int
            dict = ["Int" : array[indexPath.row] as Int]
        }
        
        NSNotificationCenter.defaultCenter().postNotificationName("attributeChange", object: nil, userInfo: dict)
    }
    
}

This code works fine but I have a problem: I don't like it. It doesn't look like Swift style modern code, it looks like a copy-paste from good old Objective-C to Swift. I dislike the fact to have an identifier property in DetailViewController: I always have to check identifier in if statements in order to perform actions. I also dislike the fact to have passedString and passedInt properties with one of them being nil in half of the cases. And, worst of it, I dislike the fact that array is of type [Any]!: it leads to many downcastings.

Is there any better way to write this code? Swift provides tools like gerenic structs and classes or protocol conformance type arrays. Can't they help write more proficient code here?

user53113