Protocols with associated types in Swift 4.2 - swift-protocols

I have a question about protocols with associated types, that occurred after updating to Xcode 10 and Swift 4.2.
Before I had a protocol:
protocol ViewModelBased: class {
associatedtype ViewModel
var viewModel: ViewModel { get set }
}
and a VC implementing it.
class MyViewController: UIViewController, ViewModelBased {
var viewModel: EntitiesViewModel!
}
After update to Xcode 10 I get and error saying MyViewController doesn't conform to the protocol and I have to declare the property as:
var viewModel: ViewModel! { get set }
Anyone has any idea why there is difference as I don't get it?

Basically your code was always dubious and now you’ve been caught:
protocol ViewModelBased: class {
associatedtype ViewModel
var viewModel: ViewModel { get set }
}
class MyViewController: UIViewController, ViewModelBased {
var viewModel: EntitiesViewModel!
}
In the protocol adopter MyViewController, what type do you claim corresponds to ViewModel? It seems you think it should be EntitiesViewModel. And the compiler permitted this to slide, allowing the implicitly unwrapped Optional wrapping a type to be substituted for the type itself.
But now there is no implicitly unwrapped Optional type; the type EntitiesViewModel! is effectively the same as the type EntitiesViewModel?, an Optional wrapping EntitiesViewModel. So if you want EntitiesViewModel to be ViewModel, and if you want this property’s type to be an Optional wrapping EntitiesViewModel, the protocol must declare this property’s type as an Optional wrapping ViewModel.

Related

'#selector' refers to a method that is not exposed to Objective-C

The new Xcode 7.3 passing the parameter via addTarget usually works for me but in this case it's throwing the error in the title. Any ideas? It throws another when I try to change it to #objc
Thank you!
cell.commentButton.addTarget(self, action: #selector(FeedViewController.didTapCommentButton(_:)), forControlEvents: UIControlEvents.TouchUpInside)
The selector it's calling
func didTapCommentButton(post: Post) {
}
In my case the function of the selector was private. Once I removed the private the error was gone. Same goes for fileprivate.
In Swift 4
You will need to add #objc to the function declaration. Until swift 4 this was implicitly inferred.
You need to use the #objc attribute on didTapCommentButton(_:) to use it with #selector.
You say you did that but you got another error. My guess is that the new error is that Post is not a type that is compatible with Objective-C. You can only expose a method to Objective-C if all of its argument types, and its return type, are compatible with Objective-C.
You could fix that by making Post a subclass of NSObject, but that's not going to matter, because the argument to didTapCommentButton(_:) will not be a Post anyway. The argument to an action function is the sender of the action, and that sender will be commentButton, which is presumably a UIButton. You should declare didTapCommentButton like this:
#objc func didTapCommentButton(sender: UIButton) {
// ...
}
You'll then face the problem of getting the Post corresponding to the tapped button. There are multiple ways to get it. Here's one.
I gather (since your code says cell.commentButton) that you're setting up a table view (or a collection view). And since your cell has a non-standard property named commentButton, I assume it's a custom UITableViewCell subclass. So let's assume your cell is a PostCell declared like this:
class PostCell: UITableViewCell {
#IBOutlet var commentButton: UIButton?
var post: Post?
// other stuff...
}
Then you can walk up the view hierarchy from the button to find the PostCell, and get the post from it:
#objc func didTapCommentButton(sender: UIButton) {
var ancestor = sender.superview
while ancestor != nil && !(ancestor! is PostCell) {
ancestor = view.superview
}
guard let cell = ancestor as? PostCell,
post = cell.post
else { return }
// Do something with post here
}
Try having the selector point to a wrapper function, which in turn calls your delegate function. That worked for me.
cell.commentButton.addTarget(self, action: #selector(wrapperForDidTapCommentButton(_:)), forControlEvents: UIControlEvents.TouchUpInside)
-
func wrapperForDidTapCommentButton(post: Post) {
FeedViewController.didTapCommentButton(post)
}
As you know selector[About] says that Objective-C runtime[About] should be used. Declarations that are marked as private or fileprivate are not exposed to the Objective-C runtime by default. That is why you have two variants:
Mark your private or fileprivate method declaration by #objc[About]
Use internal, public, open method access modifier[About]

Error in conformance of protocol to a class : Redundant conformance of "AnyClass" to protocol Equatable [duplicate]

I updated my project to Swift 2, and received a bunch of redundant conformance of XXX to protocol YYY. This happens especially often (or always) when a class conforms to CustomStringConvertible. Also some place with Equatable.
class GraphFeatureNumbersetRange: GraphFeature, CustomStringConvertible { // <--- get the error here
...
}
I suspect that I don't need to explicitly conform to a protocol when I implement var description: String { get }, or whatever methods the protocol requires. Should I just follow fixit instructions and remove all these? Does Swift now automatically infer the conformance if a class implements all the protocol's methods?
You'll get that error message in Xcode 7 (Swift 2) if a subclass declares conformance
to a protocol which is already inherited from a superclass. Example:
class MyClass : CustomStringConvertible {
var description: String { return "MyClass" }
}
class Subclass : MyClass, CustomStringConvertible {
override var description: String { return "Subclass" }
}
The error log shows:
main.swift:10:27: error: redundant conformance of 'Subclass' to protocol 'CustomStringConvertible'
class Subclass : MyClass, CustomStringConvertible {
^
main.swift:10:7: note: 'Subclass' inherits conformance to protocol 'CustomStringConvertible' from superclass here
class Subclass : MyClass, CustomStringConvertible {
^
Removing the protocol conformance from the subclass declaration
solves the problem:
class Subclass : MyClass {
override var description: String { return "Subclass" }
}
But the superclass must declare the conformance explicitly, it is
not automatically inferred from the existence of the description
property.
For googlers, I also got this error when including SwiftyJson in my Tests target and adding a swift test class, as it caused SwiftyJson to be compiled in again, and it declares NSNumber as Comparable. The solution was to only include it in the app target.
The point is that your GraphFeatureNumbersetRange is NSObject's subclass.
Which in its turn already conforms to CustomStringConvertible! That's it! Just delete this redundant protocol. Now you're declaring it twice! :-)

Redundant conformance error message Swift 2

I updated my project to Swift 2, and received a bunch of redundant conformance of XXX to protocol YYY. This happens especially often (or always) when a class conforms to CustomStringConvertible. Also some place with Equatable.
class GraphFeatureNumbersetRange: GraphFeature, CustomStringConvertible { // <--- get the error here
...
}
I suspect that I don't need to explicitly conform to a protocol when I implement var description: String { get }, or whatever methods the protocol requires. Should I just follow fixit instructions and remove all these? Does Swift now automatically infer the conformance if a class implements all the protocol's methods?
You'll get that error message in Xcode 7 (Swift 2) if a subclass declares conformance
to a protocol which is already inherited from a superclass. Example:
class MyClass : CustomStringConvertible {
var description: String { return "MyClass" }
}
class Subclass : MyClass, CustomStringConvertible {
override var description: String { return "Subclass" }
}
The error log shows:
main.swift:10:27: error: redundant conformance of 'Subclass' to protocol 'CustomStringConvertible'
class Subclass : MyClass, CustomStringConvertible {
^
main.swift:10:7: note: 'Subclass' inherits conformance to protocol 'CustomStringConvertible' from superclass here
class Subclass : MyClass, CustomStringConvertible {
^
Removing the protocol conformance from the subclass declaration
solves the problem:
class Subclass : MyClass {
override var description: String { return "Subclass" }
}
But the superclass must declare the conformance explicitly, it is
not automatically inferred from the existence of the description
property.
For googlers, I also got this error when including SwiftyJson in my Tests target and adding a swift test class, as it caused SwiftyJson to be compiled in again, and it declares NSNumber as Comparable. The solution was to only include it in the app target.
The point is that your GraphFeatureNumbersetRange is NSObject's subclass.
Which in its turn already conforms to CustomStringConvertible! That's it! Just delete this redundant protocol. Now you're declaring it twice! :-)

Method 'setChecked' with Objective-C selector 'setChecked:' conflicts with setter for 'checked' with the same Objective-C selector

I have created custom class that inherits from UIButton.
In that class I have created a function defined as:
func setChecked(checked:Bool){
self.checked = checked
if checked {
buttonImageView.image = UIImage(named: "radioSelected.png")
} else {
buttonImageView.image = UIImage(named: "radioUnselected.png")
}
}
which was working fine until I updated my xCode to 6.1.3.
Now I keep getting the error message on the function definition line:
Method 'setChecked' with Objective-C selector 'setChecked:' conflicts
with setter for 'checked' with the same Objective-C selector
I already tried to make a override it but then I get a error saying that "Method does not override any method from its superclass".
Anyone know how can I correctly solve it?
(I don't want to change the name of my function.)
You have function name conflicting with your property. What about to implement it in a more elegant way with property observing? This explicitly shows how value changes, as well as a side effects for the value changes.
class RadioButton: UIButtom {
var checked: Bool = false {
didSet {
buttonImageView.image = UIImage(named: checked ? "radioSelected.png" : "radioUnselected.png")
}
}
}
It seems that there is a name collision happening. There a few more people with the same issue:
Compiler error: Method with Objective-C selector conflicts with previous declaration with the same Objective-C selector
https://stackoverflow.com/questions/30006724/method-setplayer-with-objective-c-selector-setplayer-conflicts-with-setter
Try renaming your function to something like:
func checkedSetter(checked:Bool){

Swift weak delegate runtime error (bad access). Bug?

I have a problem with delegates in Swift (OSX). I have a view, connected to a delegate through a weak reference. Simplified code could be like this:
protocol MyProtocol: class {
func protocolFunc() -> Int
}
class MyController : MyProtocol {
func protocolFunc() -> Int { return 2 }
}
class MyView : NSView {
weak var delegate: MyProtocol?
func grabData {
var data = delegate?.protocolFunc()
}
}
When delegate?.protocolFunc() is called, the app crashes saying "bad access". It's like if the MyController instance had disappeared... But it has not. The MyController instance lives in a NSDocument subclass; and view's delegate is properly set.
The crash goes away if I declare the delegate to be strong. But the thing is I want the delegate to be weak. What's going on? To my eyes, the weak reference should work.
At the time of writing (Xcode 6 Beta 5), there's a bug with weak delegates. For the time being, all you can do until it is fixed is to change protocol MyProtocol: class to #objc protocol MyProtocol and avoid using any pure Swift classes in your protocol.
A temporary alternate solution would be to change this:
weak var delegate: MyProtocol?
to this:
weak var delegate: MyController?
Of course it defeats the purpose of MyProtocol, however, it allows you to use pure Swift classes while we wait for a proper fix for this.

Resources