Swift Protocol Property in Protocol - Candidate Has Non-Matching Type

Swift protocol conform: Candidate has non-matching type

A playground example of what you can do:

class SomeClass {

}

class Subclass : SomeClass{

}

protocol TestProtocol {
typealias T : SomeClass
var prop: [T] {get}
}

class Test: TestProtocol {
var prop = [Subclass]()

func test(){
prop.append(Subclass())
}
}

let test = Test()
test.test()
print(test.prop) // prints "[Subclass]\n"

Candidate has non-matching type of 'clearly matching type'

The answer I got to:

I'm not sure why I cannot expose the variable as it is. User isn't a protocol; it's just a subclass of NSManagedObject.

And I tested exposing a simple bool from the class and it worked just fine.

But in the end exposing the whole User object isn't quite wise, because I might need to pull all its dependencies in the dependent module even though the module itself doesn't use all of them.

So I:

  • declared a protocol (in the dependent module) used to expose what I need from User
public protocol ModuleUser {
var id: Int64
}
  • used that in the dependent protocol:
public protocol LoginAPIManagerProtocol {
...
var moduleUser: ModuleUser? { get }
...
}
  • and the conformance:
extension APIManager: LoginAPIManagerProtocol, LoginProtocol {
var moduleUser: ModuleUser? {
return self.user
}
}

And now I don't need to add the User object to the dependent module anymore; which is noice :D

Swift: Candidate has non-matching type

func numberOfSlicesInPieChartView(pieChartView: PieChartView) -> CInt {
return 32
}
func pieChartView(pieChartView: PieChartView, colorForSliceAtIndex index: UInt) -> UIColor {
return UIColor(red: 1.0, green: 0, blue: 0, alpha: 1)
}

func pieChartView(pieChartView: PieChartView, valueForSliceAtIndex index: UInt) -> Double {
return 100/3;
}

Use -> CInt instead of Int because Swift Int could not be converted to C int. Same as Double -> CDouble

Use index: UInt instead of index: Int, because NSUInteger is unsigned.

Swift protocol property of type Set Self

The way to put constraints in protocols is similar to how you specify protocol conformance (they're the same thing after all)

protocol Groupable: Hashable
{
var parent: Self? { get }
var children: Set<Self> { get }
}

Swift - Conform third-party type to my own protocol with conflicting requirement

Inspired by Jonas Maier's comment, I found what I believe to be an architecturally adequate solution to this problem. As Jonas said, function overloading exhibits the behavior that I'm looking for. I'm starting to think that maybe protocol requirements should only ever be functions, and not properties. Following this line of thinking, my protocol will now be:

protocol CursorInput {
func getCursorLocation () -> CGPoint
func setCursorLocation (_ newValue: CGPoint)
}

(Note that in this answer I'm making it settable as well, unlike in the original post.)

I can now retroactively conform AATrackpad to this protocol without conflict:

extension AATrackpad: CursorInput {
func getCursorLocation () -> CGPoint {
return CGPoint(x: self.cursorLocation.x, y: self.cursorLocation.y)
}
func setCursorLocation (_ newValue: CGPoint) {
self.cursorLocation = AAPoint(newValue)
}
}

Important - This will still compile even if AATrackpad already has a function func getCursorLocation () -> AAPoint, which has the same name but a different type. This behavior is exactly what I was wanting from my property in the original post. Thus:

The major problem with including a property in a protocol is that it can render certain concrete types literally incapable of conforming to that protocol due to namespace collisions.

After solving this in this way, I have a new problem to solve: there was a reason I wanted cursorLocation to be a property and not a function. I definitely do not want to be forced to use the getPropertyName() syntax all across my app. Thankfully, this can be solved, like this:

extension CursorInput {
var cursorLocation: CGPoint {
get { return self.getCursorLocation() }
set { self.setCursorLocation(newValue) }
}
}

This is what is so cool about protocol extensions. Anything declared in a protocol extension behaves analogously to a default argument for a function - only used if nothing else takes precedence. Because of this different mode of behavior, this property does not cause a conflict when I conform AATrackpad to CursorInput. I can now use the property semantics that I originally wanted and I don't have to worry about namespace conflicts. I'm satisfied.


"Wait a second - now that AATrackpad conforms to CursorInput, doesn't it have two versions of cursorLocation? If I were to use trackpad.cursorLocation, would it be a CGPoint or an AAPoint?

The way this works is this - if within this scope the object is known to be an AATrackpad then Alice's original property is used:

let trackpad = AATrackpad()
type(of: trackpad.cursorLocation) // This is AAPoint

However, if the type is known only to be a CursorInput then the default property that I defined gets used:

let cursorInput: CursorInput = AATrackpad()
type(of: cursorInput.cursorLocation) // This is CGPoint

This means that if I do happen to know that the type is AATrackpad then I can access either version of the property like this:

let trackpad = AATrackpad()
type(of: trackpad.cursorLocation) // This is AAPoint
type(of: (trackpad as CursorInput).cursorLocation) // This is CGPoint

and it also means that my use case is exactly solved, because I specifically wanted not to know whether my cursorInput happens to be an AATrackpad or a BBMouse - only that it is some kind of CursorInput. Therefore, wherever I am using my cursorInput: CursorInput?, its properties will be of the types which I defined in the protocol extension, not the original types defined in the class.


There is one possibility that a protocol with only functions as requirements could cause a namespace conflict - Jonas pointed this out in his comment. If one of the protocol requirements is a function with no arguments and the conforming type already has a property with that name then the type will not be able to conform to the protocol. This is why I made sure to name my functions including verbs, not just nouns (func getCursorLocation () -> CGPoint) - if any third-party type is using a verb in a property name then I probably don't want to be using it anyway :)



Related Topics



Leave a reply



Submit