Reputation: 2488
Here's some code:
import UIKit
protocol ViewModelProtocol {
var price: String { get }
}
class ViewModel: ViewModelProtocol {
var price: String {
return "$420"
}
}
class ViewController: UIViewController {
// If you change the type to ViewModel directly, no infinite loop
var viewModel: ViewModelProtocol? = nil {
didSet {
print("viewModel didSet called")
updateDisplay()
}
}
required init?(coder aDecoder: NSCoder) {
viewModel = ViewModel()
super.init(coder: aDecoder)
updateDisplay()
}
func updateDisplay() {
print("In updateDisplay()")
print("\(viewModel?.price)")
// if you access the viewModel like this, no infinite loop
// if let v = viewModel {
// print("\(v.price)")
// }
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
This code will run in an infinite loop. Specifically, it bounces between print("\(viewModel?.price)")
in updateDisplay()
and the didSet
for viewModel
.
If you change viewModel
's type to ViewModel
directly (skipping the protocol), the infinite loop disappears. Alternatively, if you unwrap viewModel
in updateDisplay()
before using it the infinite loop also disappears.
This is in Swift 2, although I haven't verified if it has the same behavior in earlier Swift. One other data point, calling methods on the protocol doesn't cause a call to didSet
.
Does this look like a Swift bug to you?
Upvotes: 4
Views: 1151
Reputation: 127
It seems that not only I have this problem with didSet{}. There is open radar for this issue: https://openradar.appspot.com/22574299
Upvotes: 2
Reputation: 1310
It's a very impressive case.
It maybe a problem with your willSet attribute with your protocol type having a readonly attribute inside. I made a lots of tests and it's being very hard to find a solutions. But, if you really need to keep going in this way... I think the follow change can help you
protocol ViewModelProtocol {
var price: String { get set }
}
class ViewModel: ViewModelProtocol {
var price: String {
get {
return "$420"
}
set {
return
}
}
//...
}
As you said... The problem only happens when try to access directly the price attribute via viewModel object
I just put my answer here, cause it didn't fit in comment field size.
But is very impressive... I will try to find a final solution. :)
Upvotes: 2