command pattern
callbackを登録するようなもの
オペレーションの呼び出しと実行を分離する
example
commandはreceiverに移譲するだけのものから、それ自身が処理をするものまである。
protocol CommandProtocol {
func exec() {}
}
class PrintCommand: CommandProtocol {
private let receiver: receiver
func exec() { receiver.print() }
}
class Receiver {
func print() {
print(nextLine)
}
}
class Invoker {
private let command: CommandProtocol
init(command: CommandProtocol) {
self.command = command
}
func acceptInput() {
command.exec()
}
}
swift の BackgroundTask は適当な時間で強制終了される。
commandパターンによりretryをサポートする。
final class BackgroundCommand {
enum Status {
case willBegin, doing, done, failed
}
private let receiver: Receiver
private let input: ReceiverInput
private var status: Status = .willBegin
private var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid
init(receiver: Receiver, input: ReceiverInput) {
self.receiver = receiver
self.input = input
}
func exec() {
status = .doing
backgroundTaskID = UIApplication.shared.beginBackgroundTask(withName: "TASK_NAME", expirationHandler: { [weak self] in self.status = .failed })
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
guard
let r = receiver,
let i = input,
let taskID = backgroundTaskID else {
self?.status = .failed
return
}
r.action(i)
UIApplication.shared.endBackgroundTask(taskID)
self?.backgroundTaskID = .invalid
self?.status = .done
}
}
func retry() {
guard status == .doing else { fatalError() } // 適当に処理
receiver.restore() // receiverの内部状態が変わるとき
status = .willBegin
exec()
}
}