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.