Hi I have a strange situation here :
OverView:
I am working on an app where user can initiate multiple operations and all these operations w
With your implementation operationFailed
:
func operationFailed(){
self.executing = false
self.finished = false
}
you break NSOperation's native logic:
Operation Dependencies
Dependencies are a convenient way to execute operations in a specific order. You can add and remove dependencies for an operation using the addDependency: and removeDependency: methods. By default, an operation object that has dependencies is not considered ready until all of its dependent operation objects have finished executing. Once the last dependent operation finishes, however, the operation object becomes ready and able to execute.
The dependencies supported by NSOperation make no distinction about whether a dependent operation finished successfully or unsuccessfully. (In other words, canceling an operation similarly marks it as finished.) It is up to you to determine whether an operation with dependencies should proceed in cases where its dependent operations were cancelled or did not complete their task successfully. This may require you to incorporate some additional error tracking capabilities into your operation objects.
By design if operation fails it should finish. But it could mark itself somehow (some special property or a cancelled
one).
Dependent operations should check is it possible to start. Something like the following should do the job:
var requireDependencesCompletion: Bool = true
override var ready: Bool {
if requireDependencesCompletion
{
for op in self.dependencies {
if op.cancelled {
cancel()
}
}
super.ready
}
Here we override ready
property to determine what should be done. If requireDependencesCompletion
is true
an operation will check all its dependences and cancel itself if one of them was cancelled.
Set requireDependencesCompletion
to true
for your typical operations and to false
to your barrier operation so it will start in any case.
Never mind I figured it out :)
Setting the finished status to true and false triggers the KVO of NSOperationQueue and hence either starts or cancels the operation of dependent operations.
If I want my operation to execute at any point whether the dependency operations finished successfully or not I can't make use of finished flag. In this case finished flag should always be true to indicate operation finished its execution.
But how will I ensure the management of decency chain for those operations that have dependencies set and really depends whether the previous operation was successful or not ?? Simple I added another variable called finishedSuccessfully to my NSOperationSubclass.
When a operation fails though it sets the finished flag to true it will set finishedSuccessfully to false. This will cause the dependent operations start method to get called.
In the start method of dependent operation, it will iterate over all the dependency operations and checks has all of them finished with finishedSuccessfully = true.
If yes that means all the dependency operations have finished execution and finished it successfully so it can start execution. On the other hand if any one of them has finishedSuccessfully = false, that means operation finished executing but failed to do whatever it was supposed to do hence this operation should also stop itself and inform its dependents that it finished with finishedSuccessfully = false.
Summary :
Operations will execute only after the execution of all the dependency operations finished executing no matter whether they finished successfully or not.
Operations which are actually concerned about the execution status of their dependency operations will check for the status and then finally decides whether to continue execution or not.
Dependency chain is maintained as well as the confirmation that sync operation executes as well :)
Here is my implementation :
import UIKit
import CoreData
import SwiftyJSON
class VMBaseOperation: NSOperation {
var finishedSuccessfully : Bool = false
var finishedStatus : Bool = false
var executionStatus : Bool = false
var retryCount : Int = 0
private (set) var requestToQueue : BaseRequest? = nil
var vmOperationCompletionBlock: ((JSON?) -> Void)?
var vmOperationFailureBlock: ((WebResponseError?) -> Void)?
override init() {
super.init()
}
convenience init(withVMRequest request : BaseRequest) {
self.init()
requestToQueue = request
}
override func start() {
if self.cancelled {
self.finished = true
return
}
//those operations which actually wants to know if all its dependency operations finished successfully or not can create a subclass of this class override start method and add the below code
for operation in self.dependencies {
if (operation as! VMBaseOperation).finishedSuccessfully == false {
self.operationFailed()
return
}
}
//others can ignore.
NSThread.detachNewThreadSelector(#selector(main), toTarget: self, withObject: nil)
self.executionStatus = true
}
override func main() {
if self.cancelled {
return
}
self.hitWebService()
}
func hitWebService(){
let webserviceManager = WebServiceManager()
webserviceManager.getResponseFromRequest(requestToQueue!) { (requset, response, data, error) in
let error = WebResponseError.checkResponse(response, request: requset, error: error)
if error != nil {
if error == WebResponseError.NO_INTERNET {
if self.vmOperationFailureBlock != nil {
self.vmOperationFailureBlock!(error)
}
self.operationFailed()
}
else{
self.retryCount += 1
if self.retryCount == 3 {
if self.vmOperationFailureBlock != nil {
self.vmOperationFailureBlock!(error)
}
self.operationFailed()
}
else{
self.hitWebService()
}
}
}
else{
if data == nil {
self.retryCount += 1
if self.retryCount == 3 {
if self.vmOperationFailureBlock != nil {
self.vmOperationFailureBlock!(nil)
}
self.operationFailed()
}
else{
self.hitWebService()
}
}
else{
let json = JSON(data: data!)
if self.vmOperationCompletionBlock != nil {
self.vmOperationCompletionBlock!(json)
}
self.operationCompleted()
}
}
}
}
override var finished: Bool {
get{
return finishedStatus
}
set{
self.willChangeValueForKey("isFinished")
finishedStatus = newValue
self.didChangeValueForKey("isFinished")
}
}
override var executing: Bool {
get{
return executionStatus
}
set{
self.willChangeValueForKey("isExecuting")
executionStatus = newValue
self.didChangeValueForKey("isExecuting")
}
}
override var asynchronous: Bool{
get{
return true
}
set{
self.willChangeValueForKey("isAsynchronous")
self.asynchronous = true
self.didChangeValueForKey("isAsynchronous")
}
}
func operationCompleted(){
self.executing = false
self.finished = true
}
func operationFailed(){
self.finishedSuccessfully = false
self.operationCompleted()
}
func operationSucceeded(){
self.finishedSuccessfully = true
self.operationCompleted()
}
}