iOS Interviews & Design Patterns / by Wayne Bishop

For iOS Developers, acquiring the skill to recognize and apply common design patterns is key. While numerous models exist, all solutions work toward building scalable apps. If you’re preparing for a technical interview, being able to identify, compare and code design patterns will directly impact your job success. In this essay, we’ll review some common models typically used in iOS Development. 

 

SPECIFIC MODELS

As mobile developers, we have the benefit of working in a unique space on a highly specialized platform. However, design patterns aren’t exclusive to iOS development. On the contrary, their popularity is based on standards developers use to communicate ideas and organize code. In many cases, this is regardless of programming language or environment. 

Navigating the world of design patterns can sometimes be a challenge. Some patterns may work differently depending on the programming language of choice (e.g. Protocols). In the realm of iOS development, there are also specific patterns that are considered best practice. 

 

OBJECTS VS. PROTOCOLS

Arguably, the most recognized way to organize code is through Object-Orientated (OO) programming. As such, an object-based model isn’t a single design pattern, but a group of concepts that form the foundation of most modern coding projects. At a basic level, code that serves a specific goal is grouped into an object (e.g. class). Equipped with specific characteristics, objects have the ability to communicate with other objects and can achieve advanced levels of functionality. They can also borrow (e.g. inherit) functionality from other objects.

Protocol-Orientated Programming offers an alternate way to organize code projects. As the name implies, the model is based on objects conforming to various rules. Rule-based features are also present in other languages such as Java and Objective-C. However, advancements in Swift take Protocols a step further by allowing them to store actions for conforming types. For example, consider isSorted. Designed as a protocol extension, the function can be used with any class where items require sorting:

//determine sort order
protocol Sortable {

    func isSorted<T: Comparable>(_ sequence: Array<T>) -> Bool
}

extension Sortable {

    func isSorted(_ sequence: Array<T>) -> Bool {

        //check trivial cases 
        guard sequence.count >= 1 else {
            return true
        }

        var index = sequence.startIndex        

        //compare sequence values
        while index < sequence.endIndex - 1 {
            if sequence[index] > sequence[sequence.index(after: index)] {
                return false
            }
            index = sequence.index(after: index)
        }

        return true

    }           
}

 

DELEGATION VS. NOTIFICATIONS

Even if you don’t know the Delegation pattern, examples can be found throughout the iOS SDK and they are fundamental to creating any app. In an object-oriented environment, delegation is nothing more than particular objects assuming shared-responsibility for certain actions (tasks). One of their main benefits is communication. With the work of certain tasks handed off to a secondary delegate, these objects often communicate the status of work in progress or completed. As iOS developers, many of us are accustomed to working with important classes such as UITableViewController and their related delegate methods. What is interesting is that developers can use delegation for more than TableViews. Consider the following design for managing long running processes:

//set conforming rules
protocol IEngineDelegate {    
    func willProcessContent(message: String)
    func didProcessContent(results: Int)
}


class IEngine {
    
    //create protocol instance
    var delegate: IEngineDelegate?
        
    //replicate long running process
    func processContent(_ element: Int) {
                
        //send message
        delegate?.willProcessContent(message: "engine processing successfully initiated..")
        
        
       //perform some basic test operation
        let output = element * 2
        
        
        /*
         note: In a real application, this content processing could be executed
         on a background thread through GCD or some other multithreaded execution.
         */
        sleep(2)
                
        //send message (on main thread)
        delegate?.didProcessContent(results: output)
    }    
}


class Main: IEngineDelegate {    
    
    let iEngine = IEngine()
        
    init() {
        iEngine.delegate = self
    }
        
    //start main process
    func processContent(withElement element: Int) {
        iEngine.processContent(element)
    }
    
    
    //MARK: - Delegate Methods
        
    //invoked prior to process start
    func willProcessContent(message: String) {
        print(message)
    }
    
    
    //invoked after process completion
    func didProcessContent(results: Int) {
        print("the result is: \(results)")
    }        
}

//test implemention
let example = Main()
example.processContent(withElement: 5)

A challenging design pattern to understand is Notifications. Unlike delegation, notifications are based on a loosely-coupled model of registration and updates. As the phrase implies, notifications are largely systems-based and don’t follow many of the conventional interactions seen in OO development. Their power comes from being able to invoke actions by (potentially) any other object in an app. Their extended reach make them the ideal tool for monitoring system-wide operations or interactions between objects that aren’t connected through object instances, protocols or delegation. 

 

WHAT TO USE WHEN

Beyond iOS specific patterns, other classic designs include Model-View Controller, Singleton and Factory. With so many patterns to choose from, how does one choose the best model for a specific problem? Here are some questions to help you define your own framework. These questions could also be used during a technical interview:

  • Is the communication between my objects tightly or loosely coupled?
  • What is the specific goal of my process or interaction? 
  • Will the solution need to process data asynchronously? 
  • Will the app reuse the same logic in different scenarios?
  • Do one or more objects track the progress of a specific action?
  • Will the solution be built on top of a traditional data-layer?