Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Designing Software with Swift (Part II): Protocol-based Design

DZone's Guide to

Designing Software with Swift (Part II): Protocol-based Design

In the second installment of this series, let's continue with good-old fashioned protocol-based design.

· Mobile Zone
Free Resource

Download this comprehensive Mobile Testing Reference Guide to help prioritize which mobile devices and OSs to test against, brought to you in partnership with Sauce Labs.

Let’s take a look at dynamic polymorphism and runtime binding. First, let’s define some simple protocols. In this and upcoming examples, we’ll borrow the original method, class, and interface naming from the original GoF design patterns book.

public protocol Observer {
typealias DataType
func update(data: DataType)
}


The first, the Observer protocol, defines the notification interface. Supporting classes receive an update via the update(_:) method. In the original GoF version, the update method doesn’t accept any data. Ours does.

public protocol Subject {
    typealias KeyType
    typealias ObserverType
    mutating func attach(key: KeyType, observer: ObserverType)
    mutating func detach(key: KeyType)
}


The next protocol defines the interface for adding observers. We define a few typealiases for the protocol, and we do change the signature a bit from the original in the signature of attach(_:observer:) and detach(_:).

Now, let’s look at the implementations:

public class ConcreteObserver : Observer {

    let name : String = String(arc4random_uniform(10000))

    public func update(data: String) {
  print("updated : \(data)")
    }
}


Nice and simple observer, right? it has a randomly generated name, used as a key on the observer when stored. When the update(_:) method is called, it prints updated , the new data, and exits.

public class ConcreteSubject<T: Observer, S: Equatable where S == T.DataType> : Subject {

  private var observers: [String: T] = [:]

  private var data : S?

  var state: S? {
    get {
    return data
  }
  set(data) {
    self.data = data
    notify()
  }
  }

  private func notify() {
    for (_, o) in observers {
      if let mystate = state {
      o.update(mystate)
   }
  }
  }

  public func attach(key: String, observer: T) {
    observers[key] = (observer)
  }

  public func detach(key: String) {
    observers.removeValueForKey(key)
  }
}


The ConcreteSubject class implements the Subject protocol, implementing the various ways to register an Observer (e.g. attach(_:) and detach(_:), a private method for notifying observers, and a property that notifies observers via that private method whenever state changes. We store observers in a dictionary indexed by observer name, and the class is genericized by <T>, which must implement the Observer protocol, and <S>, which must be the same DataType as the type used in the observer interface.

We run the code, and it outputs updated with the new text state, as expected:

let observer = ConcreteObserver()
let subject = ConcreteSubject<ConcreteObserver, String>()
subject.attach(observer.name, observer: observer)
subject.state = "some new data"


Perfectly sensible, protocol-oriented, with generic types used where appropriate. And it works, always a plus! Let’s shake things up a bit by desiging this completely differently. See how in the next installment.

Analysts agree that a mix of emulators/simulators and real devices are necessary to optimize your mobile app testing - learn more in this white paper, brought to you in partnership with Sauce Labs.

Topics:
swift ,design ,pattern

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}