Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import UIKit
- import Foundation
- import PlaygroundSupport
- import AVFoundation
- PlaygroundPage.current.needsIndefiniteExecution = true
- class AsyncOperation: Operation {
- internal enum State {
- case pending, executing, finished
- }
- private var managedKeyPaths: [KeyPath<AsyncOperation, Bool>] {
- return [\AsyncOperation.isExecuting, \AsyncOperation.isFinished]
- }
- internal var state = State.pending {
- willSet { managedKeyPaths.forEach { willChangeValue(for: $0) } }
- didSet { managedKeyPaths.forEach { didChangeValue(for: $0) } }
- }
- override var isAsynchronous: Bool {
- return true
- }
- override var isExecuting: Bool {
- return state == .executing
- }
- override var isFinished: Bool {
- return state == .finished
- }
- }
- extension Operation {
- func cancelRecursively(removingCompletions: Bool = true) {
- if removingCompletions {
- completionBlock = nil
- }
- cancel()
- dependencies.forEach { $0.cancelRecursively(removingCompletions: removingCompletions) }
- }
- }
- extension Array where Element: Operation {
- func cancel() {
- self.forEach { $0.cancel() }
- }
- }
- extension Array where Element: LessonOperation {
- func connected() -> Array {
- zip(self, self[1...]).forEach { $0.1.addDependency($0.0); $0.0.parent = $0.1 }
- return self
- }
- }
- extension ArraySlice where Element: Operation {
- func cancel() {
- self.forEach { $0.cancel() }
- }
- }
- extension OperationQueue {
- func addWithDependencies(_ op: Operation) {
- addOperation(op)
- op.dependencies.forEach { self.addWithDependencies($0) }
- }
- func addSeries(_ ops: [Operation]) {
- guard let target = ops.last else { return }
- addWithDependencies(target)
- }
- }
- protocol ReplicatableOperation {
- func replicate() -> LessonOperation
- }
- protocol ChildOperation: class {
- var parent: LessonOperation? { get set }
- }
- protocol AsyncOperationDelegate {
- func operationDidStartExecuting(_ op: Operation)
- }
- protocol MonitoredAsyncOperation: class {
- var delegate: AsyncOperationDelegate? { get set }
- }
- class LessonOperation: AsyncOperation & MonitoredAsyncOperation & ChildOperation & ReplicatableOperation {
- var parent: LessonOperation?
- var delegate: AsyncOperationDelegate?
- func replicate() -> LessonOperation {
- preconditionFailure("not implemented")
- }
- }
- extension LessonOperation {
- func restart(on queue: OperationQueue = OperationQueue.main, after timeout: Double = 0.0, completion: @escaping (LessonOperation) -> ()) {
- let rep = self.replicate()
- parent?.addDependency(rep)
- self.cancel()
- DispatchQueue.global(qos: .userInteractive).asyncAfter(deadline: .now() + timeout) {
- queue.addOperation(rep)
- completion(rep)
- }
- }
- }
- extension LessonOperation {
- func cycle(on queue: OperationQueue, onProduce: @escaping (LessonOperation) -> ()) {
- let rep = self.replicate()
- rep.parent = self.parent
- parent?.addDependency(rep)
- queue.addOperation(rep)
- onProduce(rep)
- self.completionBlock = {
- rep.cycle(on: queue, onProduce: onProduce)
- }
- }
- func stopCycle() {
- parent?.dependencies.forEach { $0.cancelRecursively() }
- }
- }
- extension LessonOperation {
- override func addDependency(_ op: Operation) {
- super.addDependency(op)
- if let op = op as? LessonOperation {
- op.parent = self
- }
- }
- }
- extension LessonOperation {
- func interrupt(with op: LessonOperation, on queue: OperationQueue, completion: @escaping (LessonOperation, LessonOperation) -> ()) {
- let rep = self.replicate()
- parent?.addDependency(rep)
- rep.addDependency(op)
- self.cancel()
- queue.addOperation(op)
- queue.addOperation(rep)
- completion(op, rep)
- }
- }
- extension LessonOperation {
- func replace(with op: LessonOperation, on queue: OperationQueue, completion: @escaping (LessonOperation) -> ()) {
- parent?.addDependency(op)
- self.cancel()
- queue.addOperation(op)
- completion(op)
- }
- }
- class GuidingOperation: LessonOperation, AVSpeechSynthesizerDelegate {
- let text: String
- let talk: AVSpeechSynthesizer
- let lang: String
- override var name: String? {
- get { return super.name ?? text }
- set { super.name = newValue }
- }
- override func replicate() -> GuidingOperation {
- return GuidingOperation(text: text, talk: talk)
- }
- func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
- state = .finished
- }
- func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didCancel utterance: AVSpeechUtterance) {
- state = .finished
- }
- func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didStart utterance: AVSpeechUtterance) {
- state = .executing
- delegate?.operationDidStartExecuting(self)
- }
- init(text: String, lang: String = "en", talk: AVSpeechSynthesizer) {
- self.text = text
- self.lang = lang
- self.talk = talk
- super.init()
- }
- override func cancel() {
- super.cancel()
- talk.stopSpeaking(at: .word)
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) { [weak self] in
- self?.state = .finished
- }
- }
- override func start() {
- guard !isCancelled else {
- state = .finished
- return
- }
- talk.delegate = self
- speak()
- }
- func speak() {
- let utterance = AVSpeechUtterance(string: text)
- let voice = AVSpeechSynthesisVoice(language: lang)
- utterance.voice = voice
- talk.speak(utterance)
- }
- }
- class ListeningOperation: GuidingOperation {
- override func replicate() -> ListeningOperation {
- return ListeningOperation(text: text, lang: lang, talk: talk)
- }
- }
- class TargetOperation: LessonOperation {
- override var name: String? {
- get { return target }
- set { super.name = newValue }
- }
- let target: String
- init(target: String) {
- self.target = target
- super.init()
- }
- override func replicate() -> TargetOperation {
- return TargetOperation(target: target)
- }
- override func start() {
- guard !isCancelled else {
- state = .finished
- return
- }
- state = .executing
- state = .finished
- }
- }
- class PauseOperation: LessonOperation {
- override func cancel() {
- super.cancel()
- state = .finished
- }
- override func main() {
- guard !isCancelled else {
- state = .finished
- return
- }
- state = .executing
- }
- override func replicate() -> PauseOperation {
- return PauseOperation()
- }
- }
- class Lesson: AsyncOperationDelegate {
- var steps = [LessonOperation]()
- var currentStep: LessonOperation?
- var onFinish: (() -> ())?
- let internalQueue = OperationQueue()
- var pause: PauseOperation?
- func pauseCurrent(skipInterrupted: Bool = false) {
- let pause = PauseOperation()
- self.pause = pause
- skipInterrupted ? replaceCurrent(with: pause) : interruptCurrent(with: pause)
- }
- func resumeCurrent() {
- pause?.cancel()
- }
- init(steps: [LessonOperation], onFinish: (() -> ())? = nil) {
- steps.forEach { addStep($0) }
- self.onFinish = onFinish
- steps.last?.completionBlock = { [weak self] in
- self?.onFinish?()
- }
- }
- func operationDidStartExecuting(_ op: Operation) {
- self.currentStep = op as? LessonOperation
- }
- func addStep(_ op: LessonOperation) {
- op.delegate = self
- steps.append(op)
- }
- func start() {
- OperationQueue.main.addSeries(steps.connected())
- }
- func cycleCurrent() {
- currentStep?.cycle(on: .main) { [weak self] produced in
- produced.delegate = self
- }
- }
- func stopCycleCurrent() {
- currentStep?.stopCycle()
- }
- func restartCurrent() {
- currentStep?.restart(on: .main, after: 0) { [weak self] produced in
- produced.delegate = self
- }
- }
- func skipCurrent() {
- currentStep?.cancel()
- }
- func replaceCurrent(with op: LessonOperation) {
- currentStep?.replace(with: op, on: .main) { [weak self] op in
- op.delegate = self
- }
- }
- func interruptCurrent(with op: LessonOperation) {
- currentStep?.interrupt(with: op, on: .main) { [weak self] op, interrupted in
- op.delegate = self
- interrupted.delegate = self
- }
- }
- }
- let talk = AVSpeechSynthesizer()
- let step1 = GuidingOperation(text: "This is the very first lesson in learning a second language, the Spanish Language!", talk: talk)
- let step2 = GuidingOperation(text: "This lesson begins with simple greetings.", talk: talk)
- let step3 = ListeningOperation(text: "¡Hola, Roberto! ¿Cómo estás?", lang: "es", talk: talk)
- let step4 = GuidingOperation(text: "Hello Roberto, how are you?", talk: talk)
- let step5 = TargetOperation(target: "Finish Lesson")
- let lesson = Lesson(steps: [step1, step3, step4, step5]) {
- print("Lesson is finished.")
- }
- lesson.start()
- lesson.pauseCurrent(skipInterrupted: true)
- lesson.resumeCurrent()
- lesson.interruptCurrent(with: step2)
- lesson.resumeCurrent()
- lesson.skipCurrent()
- lesson.cycleCurrent()
- lesson.skipCurrent()
- lesson.cycleCurrent()
- lesson.stopCycleCurrent()
- lesson.restartCurrent()
- lesson.skipCurrent()
- lesson.stopCycleCurrent()
- lesson.stopCycleCurrent()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement