DZone
Mobile Zone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
  • Refcardz
  • Trend Reports
  • Webinars
  • Zones
  • |
    • Agile
    • AI
    • Big Data
    • Cloud
    • Database
    • DevOps
    • Integration
    • IoT
    • Java
    • Microservices
    • Open Source
    • Performance
    • Security
    • Web Dev
DZone > Mobile Zone > Swiftly Becoming Confused About Protocols (Part II)

Swiftly Becoming Confused About Protocols (Part II)

How Swift can use policies in more interesting ways than just interfaces.

Christopher Lamb user avatar by
Christopher Lamb
CORE ·
Apr. 29, 16 · Mobile Zone · Tutorial
Like (1)
Save
Tweet
4.16K Views

Join the DZone community and get the full member experience.

Join For Free

In the first part of this series, we covered how to use policies as good ol' interfaces. Something we're all used to. But Swift allows policies to do much more than that — let's take a look at using protocols as policies describing type behavior.

Protocols as Policies

Okay, so let’s take a different approach. What about using protocols as policies for generics? Generics provide us with a large degree of reuse as well, and they do it at compile time, not run time. All messages are statically dispatched, and overall, generics are more optimizable.

import Foundation
import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true

struct TimerState {
  var elapsedSeconds = 0.0
  var stopTimer = false
  var restartTimer = false
}

This is the state of the timer. In the previous version, the state information was included in the Timer implementation class as object properties. In this version, we take the same variables, and wrap them into a structure. This structure would need to be exported from the module as a public type.

protocol Event {
  init(secondsElapsed: NSTimeInterval)
  var secondsElapsed: NSTimeInterval { get }
}

struct TimerEvent: Event {

  var _secondsElapsed = 0.0

  init(secondsElapsed: NSTimeInterval) {
  _secondsElapsed = secondsElapsed
  }

  var secondsElapsed: NSTimeInterval{
    get {
    return _secondsElapsed
    }
  }
}

Here, we define the event protocol to constrain our generic and a simple implementation of the protocol. We don’t use the implementation of the protocol in our class whatsoever; we only use it when we instantiate the generic class.

func defaultFirePolicy<T: Hashable, S: Event>(
myTimer: NSTimer,
inout timerState: TimerState,
timer: Timer<T, S>
) -> Void {

  if timerState.stopTimer {
    myTimer.invalidate()
    timerState.stopTimer = false
    return
  }

  if timerState.restartTimer {
    timerState.elapsedSeconds = 0
    timerState.restartTimer = false
    timer.start()
  }

  timerState.elapsedSeconds++

  timer.observers.forEach {
    let ti = NSTimeInterval(timerState.elapsedSeconds)
    let event = S(secondsElapsed: ti)
    $1(event)
  }

  if timerState.elapsedSeconds >= 10 {
  myTimer.invalidate()
  }
}

This is a default implementation for the callback handler. We define this so that users don’t need to define their own handler each time the instantiate this generic timer class.

final class Timer<T: Hashable, S> {
  var observers: [T: (S) -> Void] = [:]
  var interval: NSTimeInterval
  weak var timer: NSTimer?
  var timerState = TimerState()
  var firePolicy: (NSTimer, inout TimerState, Timer) -> Void

  init(
    interval: NSTimeInterval = 1.0,
    firePolicy: (NSTimer, inout TimerState, Timer<T, S>) -> Void = defaultFirePolicy
  ) {
    self.interval = interval
    self.firePolicy = firePolicy
  }

  func add(key: T, o:(S) -> Void) {
  observers[key] = o
  }

  func remove(key: T) {
  observers.removeValueForKey(key)
  }

  func start() {
    timer = NSTimer.scheduledTimerWithTimeInterval(
      interval,
      target: self,
      selector: Selector("timerFire:"),
      userInfo: nil,
      repeats: true
    )
  }

  func stop() {
  timerState.stopTimer = true
  }

  func restart() {
    timerState.stopTimer = true
    timerState.restartTimer = true
  }

  dynamic func timerFire(myTimer: NSTimer) {
  firePolicy(myTimer, &timerState, self)
  }
}

This is the generic timer. So what have we done here?

Well, we have a class that allows us to define the key type and the event type. We use the generic event type parameter to define the observer interface as well. The class itself is final, but we allow users to implement arbitrary customization within the timer callback. We do supply a default handler, via the defaultFirePolicy method, but that’s the only customization point we support. The implementation is final, and can’t be subclassed as a result.

var timer = Timer<String, TimerEvent>()
timer.add("l1") {
print("Seconds elapsed: \($0.secondsElapsed)")
}
timer.start()

This runs the timer with the default handler, creating this output:

Seconds elapsed: 1.0
Seconds elapsed: 2.0
Seconds elapsed: 3.0
Seconds elapsed: 4.0
Seconds elapsed: 5.0
Seconds elapsed: 6.0
Seconds elapsed: 7.0
Seconds elapsed: 8.0
Seconds elapsed: 9.0
Seconds elapsed: 10.0

Okay, so we see how it works by default, how do we add a new handler? We can use a trailing closure for that, ending up with something that looks like this:

var timer = Timer<String, TimerEvent>() {
  print("...From closure handler...")

  if $1.stopTimer {
    $0.invalidate()
    $1.stopTimer = false
    return
  }

  if $1.restartTimer {
    $1.elapsedSeconds = 0
    $1.restartTimer = false
    $2.start()
  }

  $1.elapsedSeconds++

  var timerState = $1
  $2.observers.forEach {
    let ti = NSTimeInterval(timerState.elapsedSeconds)
    let event = TimerEvent(secondsElapsed: ti)
    $1(event)
  }

  if $1.elapsedSeconds >= 10 {
  $0.invalidate()
  }
}

timer.add("l1") {
print("Seconds elapsed: \($0.secondsElapsed)")
}

timer.start()

Here, we define a trailing closure when we create the timer. When run, this yields:

...From closure handler...
Seconds elapsed: 1.0
...From closure handler...
Seconds elapsed: 2.0
...From closure handler...
Seconds elapsed: 3.0
...From closure handler...
Seconds elapsed: 4.0
...From closure handler...
Seconds elapsed: 5.0
...From closure handler...
Seconds elapsed: 6.0
...From closure handler...
Seconds elapsed: 7.0
...From closure handler...
Seconds elapsed: 8.0
...From closure handler...
Seconds elapsed: 9.0
...From closure handler...
Seconds elapsed: 10.0

This is a simple example. The key point here is that, with policy protocols, generics, and functional programming, we can design software in different ways that we’re used to, and that these new design capabilities have properties that we just didn’t have before when using interface protocols.

Now, I’m not sure which approach is better. Honestly, I think they really apply to different situations more than anything else. Do you need the kind of type abstraction interface protocols provide? Then use them for that. Do you need policy protocols for generics? Then use them. You need to be very careful how you do this though — the two approaches don’t seem mix very well.

Protocol (object-oriented programming)

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • ETL, ELT, and Reverse ETL
  • Challenges to Designing Data Pipelines at Scale
  • Implementing RBAC Configuration for Kubernetes Applications
  • The Engineer’s Guide to Creating a Technical Debt Proposal

Comments

Mobile Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • MVB Program
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends:

DZone.com is powered by 

AnswerHub logo