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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With