Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overriding default values with operation class in Swift 3

Tags:

I am attempting to override some values with the Operation class in Swift 3 as part of a conversion process from 2.2, but am encountering an issue with overriding class properties.

This is a simplified version of the code that works properly in Swift 2.2:

class ViewController: UIViewController {
    lazy var operationQueue: NSOperationQueue = {
        let queue = NSOperationQueue()
        queue.maxConcurrentOperationCount = 1

        return queue
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        callOperation()
    }

    func callOperation() {
        var error: NSError?

        let words = ["A", "B"]

        words.forEach { word in
            let operation = TestOperation(text: word)
            operation.completionBlock = {
                error = operation.error
            }
            operationQueue.addOperation(operation)
        }

        operationQueue.addOperationWithBlock { 
            if error == nil {
                print("No errors")
            } else {
                print(error?.localizedDescription)
            }
        }
    }
}

class TestOperation: NSOperation {
    private(set) var error: NSError?

    private var text: String?

    private var isExecuting: Bool = false
    private var isFinished: Bool = false

    override var asynchronous: Bool {
        return true
    }

    override var executing: Bool {
        get {
            return isExecuting
        }
        set {
            willChangeValueForKey("isExecuting")
            isExecuting = newValue
            didChangeValueForKey("isExecuting")
        }
    }

    override var finished: Bool {
        get {
            return isFinished
        }
        set {
            willChangeValueForKey("isFinished")
            isFinished = newValue
            didChangeValueForKey("isFinished")
        }
    }

    init(text: String) {
        self.text = text

        super.init()
    }

    override func start() {
        if cancelled {
            finished = true

            return
        }

        executing = true

        func completeOperation() {
            finished = true
            executing = false
        }

        let dispatchTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(1.0 * Double(NSEC_PER_SEC)))
        dispatch_after(dispatchTime, dispatch_get_main_queue(), {
            print(self.text)
            completeOperation()
        })

    }
}

Running this will produce:

A
B 
No errors

After converting it to Swift 3.0, I get the following, with the main issue being around the variables marked with comments:

class ViewController: UIViewController {
    lazy var operationQueue: OperationQueue = {
        let queue = NSOperationQueue()
        queue.maxConcurrentOperationCount = 1

        return queue
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        callOperation()
    }

    func callOperation() {
        var error: NSError?

        let words = ["A", "B"]

        words.forEach { word in
            let operation = TestOperation(text: word)
            operation.completionBlock = {
                error = operation.error
            }
            operationQueue.addOperation(operation)
        }

        operationQueue.addOperationWithBlock {
            if error == nil {
                print("No errors")
            } else {
                print(error?.localizedDescription)
            }
        }
    }
}

class TestOperation: Operation {
    private(set) var error: NSError?

    private var text: String?

//    private var executing: Bool = false // Overriding var must be as accessible as its enclosing type
//    private var finished: Bool = false // Overriding var must be as accessible as its enclosing type

//    var executing: Bool = false // Cannot override a stored property
//    var finished: Bool = false // Cannot override a stored property

    override var executing: Bool = false // Cannot override a stored property
    override var finished: Bool = false // Cannot override a stored property

    override var isAsynchronous: Bool {
        return true
    }

    override var isExecuting: Bool {
        get {
            return executing
        }
        set {
            willChangeValue(forKey: "executing")
            executing = newValue
            didChangeValue(forKey: "executing")
        }
    }

    override var isFinished: Bool {
        get {
            return finished
        }
        set {
            willChangeValue(forKey: "finished")
            finished = newValue
            didChangeValue(forKey: "finished")
        }
    }

    init(text: String) {
        self.text = text

        super.init()
    }

    override func start() {
        if isCancelled {
            isFinished = true

            return
        }

        isExecuting = true

        func completeOperation() {
            isFinished = true
            isExecuting = false
        }

        DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
            print(self.text)
            completeOperation()
        }
    }
}

If I replace the finished/executing variables with something else (myFinished, myExecuting) & update accordingly, I can run the app but I only get the following:

A

This does not allow the operation to properly finish, so subsequent operations are never run.

like image 316
CodeBender Avatar asked Oct 28 '16 17:10

CodeBender


1 Answers

So it turns out that I needed to prepend the private variables with an _ for the compiler to accept them.

The items of note have been highlighted with comments.

Below is the working code for Swift 3:

import UIKit

class ViewController: UIViewController {
    lazy var operationQueue: OperationQueue = {
        let queue = OperationQueue()
        queue.maxConcurrentOperationCount = 1

        return queue
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        callOperation()
    }

    func callOperation() {
        var error: NSError?

        let words = ["A", "B"]

        words.forEach { word in
            let operation = TestOperation(text: word)
            operation.completionBlock = {
                error = operation.error
            }
            operationQueue.addOperation(operation)
        }

        operationQueue.addOperation {
            if error == nil {
                print("No errors")
            } else {
                print(error?.localizedDescription)
            }
        }
    }
}

class TestOperation: Operation {
    private(set) var error: NSError?

    private var text: String?

    private var _executing: Bool = false // Notice the _ before the name
    private var _finished: Bool = false // Notice the _ before the name

    override var isAsynchronous: Bool {
        return true
    }

    override var isExecuting: Bool {
        get {
            return _executing
        }
        set {
            willChangeValue(forKey: "isExecuting") // This must match the overriden variable
            _executing = newValue
            didChangeValue(forKey: "isExecuting") // This must match the overriden variable
        }
    }

    override var isFinished: Bool {
        get {
            return _finished
        }
        set {
            willChangeValue(forKey: "isFinished") // This must match the overriden variable
            _finished = newValue
            didChangeValue(forKey: "isFinished") // This must match the overriden variable
        }
    }

    init(text: String) {
        self.text = text

        super.init()
    }

    override func start() {
        if isCancelled {
            isFinished = true

            return
        }

        isExecuting = true

        func completeOperation() {
            isFinished = true
            isExecuting = false
        }

        DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
            print(self.text)
            completeOperation()
        }
    }
}

This now produces the following when run:

A
B
No errors
like image 191
CodeBender Avatar answered Sep 22 '22 16:09

CodeBender