-
Notifications
You must be signed in to change notification settings - Fork 44
/
Copy pathContents.swift
170 lines (124 loc) · 4.37 KB
/
Contents.swift
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
import Foundation
import AVFoundation
// WARNING: Before proceeding please add two .mp3 files at the `Resources` folder of this .playground page (by clicking CMD+1 and unfolding the Resources directory). One file should be named `anotherSoundName.mp3` and an another one is `soundName.mp3`. You need to do that in order to be able to fully interact with the presented development here.
protocol Playable {
var player: AVAudioPlayer { get set }
}
protocol PlayerState {
func prepare()
func play()
func stop()
}
extension PlayerState {
func prepare() { /* empty implementation */ }
func play() { /* empty implementation */ }
func stop() { /* empty implementation */ }
}
struct PrepareState: PlayerState, Playable {
// MARK: - Conformance to Playable protocol
var player: AVAudioPlayer
// MARK: - Conformance to PlayerState protocol
func prepare() {
print("IdleState -> idle")
player.prepareToPlay()
}
}
struct PlayState: PlayerState, Playable {
// MARK: - Conformance to Playable protocol
var player: AVAudioPlayer
// MARK: - Conformance to PlayerState protocol
func play() {
print("PlayState -> play")
player.play()
}
}
struct StopState: PlayerState, Playable {
// MARK: - Conformance to Playable protocol
var player: AVAudioPlayer
// MARK: - Conformance to PlayerState protocol
func stop() {
print("StopState -> stop")
player.stop()
}
}
class Song {
private(set) var name: String
private(set) var file: URL
init(name: String, file: URL) {
self.name = name
self.file = file
}
}
class PlayerContext {
// MARK: - Properties
private var audioPlayer: AVAudioPlayer = AVAudioPlayer.init()
private(set) var state: PlayerState
var song: Song {
didSet {
preparePlayer()
stop()
state.prepare()
}
}
// MARK: - Initializers
init(song: Song) {
do {
try AVAudioSession.sharedInstance().setActive(true)
} catch let error {
print(error.localizedDescription)
}
state = PrepareState(player: audioPlayer)
self.song = song
preparePlayer()
}
// MARK: - Methods
func play() {
guard state is StopState || state is PrepareState else { return }
state = PlayState(player: audioPlayer)
state.play()
}
func stop() {
guard state is PlayState else { return }
state = StopState(player: audioPlayer)
state.stop()
}
// MARK: - Private methods
private func preparePlayer() {
do {
audioPlayer = try AVAudioPlayer(contentsOf: song.file, fileTypeHint: AVFileType.mp3.rawValue)
} catch {
print(error.localizedDescription)
}
}
}
//: Usage :
guard let url = Bundle.main.url(forResource: "soundName", withExtension: "mp3") else {
fatalError("Could not find an .mp3 resource in the specified Bundle or resource name")
}
guard let anotherUrl = Bundle.main.url(forResource: "anotherSoundName", withExtension: "mp3") else {
fatalError("Could not find an .mp3 resource in the specified Bundle or resource name")
}
let song = Song(name: "Music", file: url)
let anotherSong = Song(name: "Another Music", file: anotherUrl)
let playerContext = PlayerContext(song: song)
playerContext.play()
playerContext.play() // intentionally called play for the second time
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 10) {
playerContext.stop()
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 5) {
playerContext.play()
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 10) {
playerContext.stop()
playerContext.song = anotherSong
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) {
playerContext.play()
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 10) {
playerContext.stop()
}
}
}
}
}
// In order to run indefinetley we need to tell the Playgrounds that we need indefinite execution
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true