Tips and Tricks

Removing String Literals

Tips and Tricks
1 Removing String Literals 03:54

I'm not a fan of random string literals in a project and I always try to find solutions to avoid them. In today's episode, I show you an elegant solution to rid a project of string literals.

Table View Cells

I've created a simple application that shows a list of quotes in a table view. The QuotesViewController class is responsible for populating the table view with data.

The implementation of the QuotesViewController class isn't important. We are interested in the first few lines of the tableView(_:cellForRowAt:) method. We invoke dequeueReusableCell(withIdentifier:for:) on the table view to ask it for a reusable table view cell. The first parameter of the dequeueReusableCell(withIdentifier:for:) method is of type String. It's the reuse identifier of the table view cell. The current implementation uses a string literal. While it may seem unavoidable to use a string literal, there's a simple technique to get rid of the string literal.

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let cell = tableView.dequeueReusableCell(withIdentifier: "QuoteTableViewCell", for: indexPath) as? QuoteTableViewCell else {
        fatalError("Unable to Dequeue Quote Table View Cell")
    }

    ...
}

The solution is surprisingly simple. Open QuoteTableViewCell.swift and declare a static computed property, reuseIdentifier, of type String. The implementation is straightforward. The string the computed property returns is equal to the name of the class.

import UIKit

class QuoteTableViewCell: UITableViewCell {

    // MARK: - Static Properties

    static var reuseIdentifier: String {
        return String(describing: self)
    }

    ...

}

We can use the reuseIdentifier computed property to remove the string literal in the QuotesViewController class.

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let cell = tableView.dequeueReusableCell(withIdentifier: QuoteTableViewCell.reuseIdentifier, for: indexPath) as? QuoteTableViewCell else {
        fatalError("Unable to Dequeue Quote Table View Cell")
    }

    ...
}

This looks much better. But we can take it one step further. Create a new Swift file with name UITableViewCell.swift. Add an import statement for UIKit and create an extension for the UITableViewCell class.

import UIKit

extension UITableViewCell {

}

Move the reuseIdentifier computed property from QuoteTableViewCell.swift to the extension in UITableViewCell.swift.

import UIKit

extension UITableViewCell {

    // MARK: - Static Properties

    static var reuseIdentifier: String {
        return String(describing: self)
    }

}

Every UITableViewCell subclass now has a static computed property with name reuseIdentifier. It's important to emphasize that this technique only works if the reuse identifier of the table view cell is equal to the name of the UITableViewCell subclass.

View Controllers

We can apply the same technique in several other scenarios. Open QuotesViewController.swift and navigate to the implementation of the tableView(_:didSelectRowAt:) method.

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    tableView.deselectRow(at: indexPath, animated: true)

    // Initialize Quote View Controller
    guard let quoteViewController = UIStoryboard(name: "Main", bundle: .main).instantiateViewController(withIdentifier: "QuoteViewController") as? QuoteViewController else {
        return
    }

    ...
}

We instantiate a QuoteViewController instance by asking the main storyboard for a view controller with a particular identifier. The identifier of the view controller is a string literal and it's defined in the storyboard. Getting rid of the string literal is easy.

Create a new Swift file and name it UIViewController.swift. Add an import statement for UIKit and create an extension for the UIViewController class.

import UIKit

extension UIViewController {

}

We define a static computed property, storyboardIdentifier, of type String. The string the computed property returns is equal to the name of the class.

import UIKit

extension UIViewController {

    // MARK: - Static Properties

    static var storyboardIdentifier: String {
        return String(describing: self)
    }

}

We can use the computed property in QuotesViewController.swift to get rid of the string literal in tableView(_:didSelectRowAt:).

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    tableView.deselectRow(at: indexPath, animated: true)

    // Initialize Quote View Controller
    guard let quoteViewController = UIStoryboard(name: "Main", bundle: .main).instantiateViewController(withIdentifier: QuoteViewController.storyboardIdentifier) as? QuoteViewController else {
        return
    }

    ...
}

Conclusion

The combination of extensions and static computed properties is a simple and elegant solution to rid a project of string literals. I use this technique in every project I work on. Give it a try and let me know what you think.

Stop Writing Swift That Sucks

Download the Swift Patterns I Swear By