Overriding default values with operation class in Swift 3

ios, nsoperation, swift, swift3

Solution

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

Problem

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.

Original source