I recently had a short twitter conversation with Peter Steinberger about design patterns in the iOS world. My stance was that most real world apps suffer from too little software design rather than too much. I have seen a lot of projects from big and small companies – and while structural and creational design patterns are used implicitly and often unknowingly by the developers – such as adapters to encapsulate network transactions or the infamous singleton to create a globally shared instance of an object. Behavioral patterns are rarely, if at all, used in the mobile world. I think that's sad because they provide a simple, readable and testable way to encapsulate functionality.

My favorite and most-used pattern is, at least in the iOS context, the strategy pattern.

The strategy pattern is useful in situations where you might to encapsulate some special behavior that must be exchangable. An example is a search with several scope buttons, in my example it's a search for geographical locations with the ability to filter lexically (match strings) or by geocoding the users input and then filter the items that are near to the resolved address. These are two distinct algorithms or strategies.

Prior to using a strategy, our code might look (highly simplified) something like this

enum SearchMode {
    case Lexical
    case Address
}

class ViewController: UIViewController {

    let segmentedControl = UISegmentedControl()

    var searchMode = SearchMode.Lexical

    override func viewDidLoad() {
        super.viewDidLoad()
        self.segmentedControl.insertSegmentWithTitle("Lexical Search", atIndex: 0, animated: false)
        self.segmentedControl.insertSegmentWithTitle("Search near Address", atIndex: 0, animated: false)
        self.segmentedControl.addTarget(self, action: "segmentChanged:", forControlEvents: .ValueChanged)
    }

    func segmentChanged(sender: UISegmentedControl){
        if (segmentedControl.selectedSegmentIndex == 0){
            self.searchMode = .Lexical
        } else {
            self.searchMode = .Address
        }

        self.performSearch()
    }

    func searchTermChanged(term: String){
        self.performSearch()
    }

    private func performSearch(){
        if (self.searchMode == .Lexical){
            // perform a lexical search
        } else {
            // do some reverse geocoding, filter the results accordingly.
        }
    }
}

That looks like reasonable code. But it has a certain code smell. This is – at least for me – mainly caused by the performSearch method, that has too many bad aspects. First, it encapsulates two different algorithms in one method. Granted, one would probably extract the exact search implementations out to different methods, but it still has this if and .. we can do better. By extracting the whole search out.


protocol SearchStrategy {
    func updateSearchTerm(term: String)
    func filterItems(items: [Item]) -> [Item]
}

class LexicalSearchStrategy : SearchStrategy {
    func updateSearchTerm(term: String){

    }
    func filterItems(items: [Item]) -> [Item] {
        // Great lexical search.
    }
}

class GeoSearchStragey : SearchStrategy {
    func updateSearchTerm(term: String){

    }
    func filterItems(items: [Item]) -> [Item] {
        // Address search search.
    }
}

Now, there are doubtlessly some more lines here. But, in my opinion, the intent is perfectly clear, even if you just stumble over that piece, it's extensible and you can easily test the strategies in an isolated manner, which might be a requirement for more unit test scenarios. Updating the controller with the newly created strategies also simplifies the controller a bit.

class ViewController: UIViewController {

    let segmentedControl = UISegmentedControl()

    var strategy : SearchStrategy = LexicalSearchStrategy()

    override func viewDidLoad() {
        super.viewDidLoad()
        self.segmentedControl.insertSegmentWithTitle("Lexical Search", atIndex: 0, animated: false)
        self.segmentedControl.insertSegmentWithTitle("Search near Address", atIndex: 0, animated: false)
        self.segmentedControl.addTarget(self, action: "segmentChanged:", forControlEvents: .ValueChanged)
    }

    func segmentChanged(sender: UISegmentedControl){
        if (segmentedControl.selectedSegmentIndex == 0){
            self.strategy = LexicalSearchStrategy()
        } else {
            self.strategy = GeoSearchStragey()
        }

        self.performSearch()
    }

    func searchTermChanged(term: String){
        self.strategy.updateSearchTerm(term)
        self.performSearch()
    }

    private func performSearch(){
        items = self.strategy.filterItems(self.allItems)
    }
}

Note that the SearchMode-Enum is gone for good as well. Depending on the complexity of the scenario you are implementing, this might just be a handy tool in your belt to simplify your controllers and make your code smell a little less.

Tomorrow I'll shed some light on why the State pattern is completely awesome.