Kapil
Kapil

Reputation: 143

iOS swift Operation Queue

I am working on iOS app where I am using OperationQueue. I have created 2 operations. Operation2 is dependent on completion of Operation1.Operation2 needs to wait until Operation 1 get finished if its running. If operation 1 is not running then operation 2 should start immediately.

Its not working as expected so I am testing in playground

    class MyManager {

        var operationQueue: OperationQueue?

        var operation1: MyOperation? = nil
        var operation2: MyOperation? = nil

        typealias completion = (_ serverError: String?) -> Void

        func talkWithServer(completion: completion?) {

            completion?("competed!")
        }

        func doOperation1() {

            cancelProcess()
            setup()

            guard let operation1 = self.operation1 else { return }
            operation1.codeToRun = {
                print("operation1 started")
                self.talkWithServer(completion: { (completion) in
                    print("operation1 completed")
                    operation1.markAsFinished()
                })
            }
            operationQueue?.addOperation(operation1)

        }


        func doOperation2() {
           self.operation2 = MyOperation()

            guard let operation2 = self.operation2 else { return }



            operation2.codeToRun = {
                print("operation2 started")
                self.talkWithServer(completion: { (completion) in
                    print("operation2 completed")
                    operation2.markAsFinished()
                })
            }

if let operation1 = self.operation1 {
            if operation1.isExecuting {
                operation2.addDependency(operation1)
                operation1.completionBlock = {
                    print("operation1.completionBlock")
                    self.operationQueue?.addOperation(operation2)
                }
            }
            } else  {
                operationQueue?.addOperation(operation2)
            }

        }


        func cancelProcess() {
            print("cancelAllOperations")
            operationQueue?.cancelAllOperations()
        }

        func setup() {
            print("setup Called")
            operationQueue?.cancelAllOperations()
            operationQueue = OperationQueue()
            operation1 = MyOperation()
            operation2 = MyOperation()
        }
    }


    class MyOperation: Operation {
        var codeToRun: (()->Void)?

        var _executing = false
        var _finished = false

        override internal(set) var isExecuting: Bool {
            get {
                return _executing
            }
            set {
                _executing = newValue

            }
        }

        override internal(set) var isFinished: Bool {
            get {
                return _finished
            }
            set {
                _finished = newValue
            }
        }

        override var isAsynchronous: Bool {
            return true
        }

        override func start() {
            isExecuting = true
            isFinished = false
            if let closure = self.codeToRun {
                closure()
            }
        }

        func markAsFinished() {
            self.isExecuting = false
            self.isFinished = true
            completionBlock?()
        }
    }

    let manager = MyManager()

    manager.doOperation1()
    manager.doOperation2()

I am getting result

cancelAllOperations
setup Called
operation1 started
operation1 completed
operation1.completionBlock

Expected is

cancelAllOperations
setup Called
operation1 started
operation1 completed
operation1.completionBlock
operation2 started
operation2 completed

Am I missing here anything?

Upvotes: 2

Views: 6738

Answers (3)

Dhaval H. Nena
Dhaval H. Nena

Reputation: 4120

All you need to do is just add dependency on the dependent operation.

let queue = OperationQueue()

let operation1 = BlockOperation(block: { [weak self] in
    self?.doOperation1()
})

let operation2 = BlockOperation(block: { [weak self] in
    self?.doOperation2()
})

operation1.addDependency(operation2) // THIS IS THE KEY CODE IN YOUR CASE

queue.addOperation(operation1)
queue.addOperation(operation2)

Hope this may help you solve your dependency problem.

Upvotes: 0

Leacode  Lea
Leacode Lea

Reputation: 21

I've been looking at your code. I found a few things:

First

manager.doOperation1()
manager.doOperation2()

this not means operation2 runs after operation1 finished, if you want to do this, you can add a completion closure for operation1.

Second

when you call

doOperation2()

in this function seems like the code never executed after:

guard let operation2 = self.operation2 else { return }

After all

It seems like you want to create your own wheels. I suggest you to learn something about GCD,you can find resources here:

Grand Central Dispatch Crash Course for Swift 3

Grand Central Dispatch Tutorial for Swift 3: Part 1/2

Upvotes: 1

user1046037
user1046037

Reputation: 17685

There are a couple of things:

Implementation:

  • Implement KVO for isExecuting and isFinished
  • Fix doOperation2
  • Once you set the dependencies the operation2 wouldn't start even if it is added to the queue until operation1 completes.
  • Check for isCancelled inside MyOperation

Below is not the ideal way to implement doOperation2, but removes some of the clutter from your code. I will leave it to you to implement the whole thing based on the Design section mentioned below.

func doOperation2() {

    self.operation2 = MyOperation()

    guard let operation2 = self.operation2 else {
        return
    }

    operation2.codeToRun = {
        print("operation2 started")

        self.talkWithServer(completion: { (completion) in
            print("operation2 completed")
        })
    }

    operationQueue?.addOperation(operation2)
}

Design:

  • In your implementation, MyOperation seems to be generic and you seem to be doing most of the real work at the place where you call them
  • Modify MyOperation to do the real work.
  • Call site should be simple
  • Example FetchData() is an operation, ParseData() is another operation.
  • So an operation contains business logic.
  • At the call site you can just add dependencies.

Upvotes: 0

Related Questions