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.