mirror of
https://github.com/samsonjs/Advanced-NSOperations.git
synced 2026-03-25 08:25:47 +00:00
110 lines
3.8 KiB
Swift
110 lines
3.8 KiB
Swift
/*
|
||
Copyright (C) 2015 Apple Inc. All Rights Reserved.
|
||
See LICENSE.txt for this sample’s licensing information
|
||
|
||
Abstract:
|
||
This file contains the fundamental logic relating to EarthquakeOperation conditions.
|
||
*/
|
||
|
||
import Foundation
|
||
|
||
let OperationConditionKey = "OperationCondition"
|
||
|
||
/**
|
||
A protocol for defining conditions that must be satisfied in order for an
|
||
operation to begin execution.
|
||
*/
|
||
protocol OperationCondition {
|
||
/**
|
||
The name of the condition. This is used in userInfo dictionaries of `.ConditionFailed`
|
||
errors as the value of the `OperationConditionKey` key.
|
||
*/
|
||
static var name: String { get }
|
||
|
||
/**
|
||
Specifies whether multiple instances of the conditionalized operation may
|
||
be executing simultaneously.
|
||
*/
|
||
static var isMutuallyExclusive: Bool { get }
|
||
|
||
/**
|
||
Some conditions may have the ability to satisfy the condition if another
|
||
operation is executed first. Use this method to return an operation that
|
||
(for example) asks for permission to perform the operation
|
||
|
||
- Parameter operation: The `EarthquakeOperation` to which the Condition has been added.
|
||
- Returns: An `Operation`, if a dependency should be automatically added. Otherwise, `nil`.
|
||
- Note: Only a single operation may be returned as a dependency. If you
|
||
find that you need to return multiple operations, then you should be
|
||
expressing that as multiple conditions. Alternatively, you could return
|
||
a single `GroupOperation` that executes multiple operations internally.
|
||
*/
|
||
func dependencyForOperation(operation: EarthquakeOperation) -> Operation?
|
||
|
||
/// Evaluate the condition, to see if it has been satisfied or not.
|
||
func evaluateForOperation(operation: EarthquakeOperation, completion: @escaping (OperationConditionResult) -> Void)
|
||
}
|
||
|
||
/**
|
||
An enum to indicate whether an `OperationCondition` was satisfied, or if it
|
||
failed with an error.
|
||
*/
|
||
enum OperationConditionResult: Equatable {
|
||
case Satisfied
|
||
case Failed(NSError)
|
||
|
||
var error: NSError? {
|
||
if case .Failed(let error) = self {
|
||
return error
|
||
}
|
||
|
||
return nil
|
||
}
|
||
}
|
||
|
||
func ==(lhs: OperationConditionResult, rhs: OperationConditionResult) -> Bool {
|
||
switch (lhs, rhs) {
|
||
case (.Satisfied, .Satisfied):
|
||
return true
|
||
case (.Failed(let lError), .Failed(let rError)) where lError == rError:
|
||
return true
|
||
default:
|
||
return false
|
||
}
|
||
}
|
||
|
||
// MARK: Evaluate Conditions
|
||
|
||
struct OperationConditionEvaluator {
|
||
static func evaluate(conditions: [OperationCondition], operation: EarthquakeOperation, completion: @escaping ([NSError]) -> Void) {
|
||
// Check conditions.
|
||
let conditionGroup = DispatchGroup()
|
||
|
||
var results: [OperationConditionResult?] = Array(repeating: nil, count: conditions.count)
|
||
|
||
// Ask each condition to evaluate and store its result in the "results" array.
|
||
for (index, condition) in conditions.enumerated() {
|
||
conditionGroup.enter()
|
||
condition.evaluateForOperation(operation: operation) { result in
|
||
results[index] = result
|
||
conditionGroup.leave()
|
||
}
|
||
}
|
||
|
||
// After all the conditions have evaluated, this block will execute.
|
||
conditionGroup.notify(queue: DispatchQueue.global(qos: .default)) {
|
||
// Aggregate the errors that occurred, in order.
|
||
var failures = results.compactMap { $0?.error }
|
||
|
||
/*
|
||
If any of the conditions caused this operation to be cancelled,
|
||
check for that.
|
||
*/
|
||
if operation.isCancelled {
|
||
failures.append(NSError(code: .ConditionFailed))
|
||
}
|
||
|
||
completion(failures)
|
||
}
|
||
}
|
||
}
|