Jan Gorman

Buttery smooth scrolling!

Design Patterns in Swift: Adapter Pattern

Already December! Time for a new chapter on applying design patterns in Swift. This time it’s the Adapter Pattern’s turn. Good stuff! And simple!

So what is it and what can it do for you? The Adapter Pattern converts the interface of one class into another interface. That’s it. And with this simple pattern you can let classes work together that otherwise couldn’t.

So let’s assume you have the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
protocol MusicPlayer {

    func insertMedia()
    func play()

}

class CassettePlayer: MusicPlayer {

    func insertMedia() {
        println("Insert mixtape")
    }

    func play() {
        println("Push down clunky button and play")
    }

}

Clearly, this kind if thing is only acceptable in the 80s.

Fast forward to now, Spotify, beats, you name it. How do you travel back in time and get a client that accepts a MusicPlayer to stream and play a track? You adapt it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
protocol MediaPlayer {

    func getMedia(title: String)
    func play()

}

class StreamingMediaPlayer: MediaPlayer {

    func getMedia(title: String) {
        println("Acquiring \(title) from the cloud")
    }

    func play() {
        println("Playing!")
    }

}

// MARK: Adapter in action

class StreamingMediaPlayerAdapter: MusicPlayer {

    let player: StreamingMediaPlayer

    init(player: StreamingMediaPlayer) {
        self.player = player
    }

    func insertMedia(title: String) {
        player.getMedia(title)
    }

    func play() {
        player.play()
    }

}

Jaws drop in amazement. But now consider your existing project that you want to spice up with some of that Swift. You’ve written your amazing new classes, time to integrate with Objective-C. So you head over to Using Swift with Cocoa and Objective-C only to find that the new goodness doesn’t all work. All of these aren’t compatible with Objective-C:

  • Generics
  • Tuples
  • Enumerations defined in Swift
  • Structures defined in Swift
  • Top-level functions defined in Swift
  • Global variables defined in Swift
  • Typealiases defined in Swift
  • Swift-style variadics
  • Nested types
  • Curried functions

Not to despair though, you can still go ahead and be as idiomatic in Swift as you want. With some extra effort you can get the old to talk to the new. Consider a Swift only protocol that just needs to use a tuple:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
protocol Legend {

    func doSomething() -> (String, String)

}

class IAmLegend: Legend {

    func doSomething() -> (String, String) {
        return ("very", "important")
    }

}

func doSomethingWithLegend(legend: Legend) {
    let (first, second) = legend.doSomething()
    // Do something else with these variables
}

Important stuff going on. You get the idea. But obviously you won’t be able to call that from any existing code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@objc class Compatible {

    let first: String
    let second: String

    init(first: String, second: String) {
        self.first = first
        self.second = second
    }

}

class LegendSwiftAdapter: Legend {

    let compatible: Compatible

    init(compatible: Compatible) {
        self.compatible = compatible
    }

    func doSomething() -> (String, String) {
         return (compatible.first, compatible.second)
    }

}

@objc class LegendObjcAdapter {

    func doSomethingWithATuple(compatible: Compatible) {
        let adapter = LegendSwiftAdapter(compatible)
        doSomethingWithLegend(adapter)
    }

}

I hope it’s somewhat clear what’s going on. Contrived example maybe‚Ķ I’ll upload a more practical application soon-ish. Anyways, that’s the Adapter Pattern applied in Swift for you. As always the code is available as Xcode playground on github.