Objective-C conflict error

On a MacBook, running macOS 10.15.7 with Xcode 12.1, I wrote some classes in Swift that look like the following:

class A {
    @objc private var value: NSNumber {
        get { ... }
        set { ... }
    }
}

class B: A {
    @objc private var value: String {
        get { ... }
        set { ... }
    }
}

The property is marked @objc so I can bind an input to it. The code compiles and runs perfectly fine without issue, with the input properly binding to the value for either class, and I committed it to my repository.

I later checked out the repository on an iMac, also running macOS 10.15.7 with Xcode 12.1, and it refuses to compile with the following errors:

Getter for 'value' with Objective-C selector 'value' conflicts with getter for 'value' from superclass 'A' with the same Objective-C selector

Setter for 'value' with Objective-C selector 'setValue:' conflicts with setter for 'value' from superclass 'A' with the same Objective-C selector

Why am I getting the errors on one computer but not another, with the exact same project and software?? I'm sure I could rework it somehow to avoid the error, but it would be annoying to do so, especially when I don't really understand why I should rework it, given that it works fine on one machine.

(This is a cross-post from Stack Overflow where I didn't get any answers)

I don't know why it doesn't always produce a compile error. That sounds like a bug. One question, though: on what source line does the error appear? On the method declaration, or at a call site (or other point of use)?

I think your approach is a poor solution. A value binding itself is typeless, so defining properties with specific types is going to fail spectacularly if a wrongly-typed instance is given to the binding by mistake.

You would probably be better served by a single property (of type Any or possibly AnyObject) for the binding, and other type-specific computed properties to cast the bound instance at run time, with deterministic handling if the wrong type is bound.

It's also a poor solution in another way. Your two properties are unrelated in Swift, but related in Obj-C (B.value overrides A.value in Obj-C). That difference in semantics between the two languages is very likely going to jump up and bite you later.

The error appears on the declaration.

I think your approach is a poor solution. A value binding itself is typeless, so defining properties with specific types is going to fail spectacularly if a wrongly-typed instance is given to the binding by mistake

Not quite sure what you mean, are you suggesting value bindings should never be typed? That seems contrary to my entire understanding of bindings. The binding figures out what the type is at runtime and converts primitives accordingly - that's what it's designed to do, no?

Your two properties are unrelated in Swift, but related in Obj-C ( B.value overrides A.value in Obj-C).

Right, this is perhaps something I don't fully understand. How does a private property get translated into Obj-C? Does it become like a property in a private header, or an instance variable, or something else?

No, not really. The binding uses KVC to access properties, and KVC will automatically convert between an object value and a scalar (non-object-valued) property value (for some simple scalar property types). However, if your property is object-valued (NSNumber or NSString) then there's no conversion.

Normally this isn't an issue, because you'd control what type is at the other end of the binding (e.g. binding to a text field is NSString, unless it has a numeric formatter, in which case it's NSNumber). But because a B is also an A, you run the risk of using the wrong kind of value object for the bound property.

The error message is telling you that both Swift properties would be translated into the same Obj-C property name (value), the same name you're using in your binding.

In Obj-C, properties are really just methods (getter and setter methods), and Obj-C methods override purely by name (selector). Therefore, defining the methods like you have in Swift will result in Obj-C overrides that you didn't intend. That's why the compiler won't let you do it.

As I said before, you can solve this in Swift by using a single value method marked @objc, but you can only have one return type, so it'd have to return Any, and you'd get a string or number by explicit casting with as?.

Normally this isn't an issue, because you'd control what type is at the other end of the binding (e.g. binding to a text field is NSString , unless it has a numeric formatter, in which case it's NSNumber ). But because a B is also an A , you run the risk of using the wrong kind of value object for the bound property.

Yes, exactly, each class is worrying about its own formatter so I have to disagree that this is any more "risky" than any other key-value binding, or in fact anything that relies on the dynamic nature of Obj-C.

Not sure what I did (nothing) but the errors have now disappeared on the second machine...

Terms of Service

Privacy Policy

Cookie Policy