How did Apple bridge this struct to Objective-C

You might also want to look into the ReferenceConvertible protocol. It refines the _ObjectiveCBridgeable protocol (albeit, Apple documentation and Xcode won't show that fact) and adds certain requirements that are important for value semantics.

The _ObjectiveCBridgeable protocol has some very important documentation regarding the intended usage of its requirements (which sadly is impossible to see without looking at the original source file containing the protocol).

3 Likes

Aha, this effectively sidesteps the whole issue of _ObjectiveCBridgeable being private (or "deferred")...

In addition to what _ObjectiveCBridgeable wants, ReferenceConvertible requires my type to have a few extra bells and whistles ("description" / "debugDescription" / "NSCopyablility") but that's not a big difference.

1 Like

I somehow can't get ReferenceConvertible working. I added description, debugDescription and typealias ReferenceType. Xcode tells me it isn't conforming to the protocol but doesn't suggest what's missing.

But I prefer sticking to _ObjectiveCBridgeable anyway as Apple uses it on all their newer API`s while ReferenceConvertible hasn't been used for a long time by Apple. I can only find some of the initial Swift types/objects conforming to it (like Date, etc.)

I don't know if there's any code that is generic over conformance to ReferenceConvertible, so I don't see what you'd be missing out on if you skipped that conformance.

Having said that, it does require the ReferenceType to be NSCopying, so that might be what's wrong with your conformance.

Thats might be it. Do I have to confirm the Swift or Objective-C object to NSCopying?

Most likely both. We’ve talked about redoing the runtime so the protocol isn’t involved, because looking up the conformance is relatively expensive.

3 Likes

Okay. I will stay with _ObjectiveCBridgeable :smiley:

I was talking about _ObjectiveCBridgeable

I didn't find any examples of either, just let Xcode "fix-it" fill up the required conformances stubs and then implemented those conformances († except for the mentioned NSCopying that fix-it didn't suggest so I added that manually), after that it worked fine for me.

Note that as it stands "_ObjectiveCBridgeable" is private API and "ReferenceConvertible" is public, so until those in the know shed more light on it I'd say "ReferenceConvertible" is a better choice amongst the two.

Yep, that must be it (see † above).

Obj-C:

@interface NSCellConfigurationStateObjc : NSObject<NSCopying>
// ...
@end

@implementation NSCellConfigurationStateObjc
// ...
- (nonnull id)copyWithZone:(nullable NSZone *)zone {
    return [[NSCellConfigurationStateObjc allocWithZone:zone] initWithIsSelected:self.isSelected 
      isEditing:self.isEditing];
}

Swift:

class C: NSObject, NSCopying {
    var field: Int = 0
    init(field: Int) {
        self.field = field
    }
    
    func copy(with zone: NSZone? = nil) -> Any {
        precondition(zone == nil, "TODO: will non zero NSZone ever happen? is it fine to ignore it?") // ††
        return C(field: field)
    }
}

I'd only worry about †† precondition if it ever happens. Once (and if) it happens you could properly test that use case.

1 Like

I don't think you need to check zone with a precondition, just ignore it.

zone
This parameter is ignored. Memory zones are no longer used by Objective-C.

2 Likes

Do not add your own conformances to _ObjectiveCBridgeable. The compiler will crash. The runtime will do the wrong thing. It may appear to work in one configuration but break when you change seemingly unrelated compiler flags, deploy to a different OS version, or any reason. As David noted, the protocol is on the chopping block and may be ignored completely by future runtimes.

8 Likes

I switched to ReferenceConvertible. It works. Only the Objective-C class has to conform to NSCopying.

1 Like

That makes sense, but how should we treat the ReferenceConvertible protocol? its documentation says:

A decoration applied to types that are backed by a Foundation reference type.

Does this mean it's not intended to have conformances outside of Foundation? If so, why wouldn't it be underscored like the _Pointer protocol in the standard library? And if it's not intended to be internal-only, then how should we treat the fact that it refines _ObjectiveCBridgeable?

2 Likes

The ReferenceConvertible protocol doesn't itself have any special treatment from the compiler, but it looks like it is defined in Foundation as refining _ObjectiveCBridgeable, so I would extremely strongly advise against its use as well.

2 Likes

I think ReferenceConvertible is mostly public so that you can understand the bridging from foundation structs to their objective-c classes in the documentation.

ReferenceConvertible is public API. It's used outside of Foundation, e.g. SwiftUI uses it:

extension SwiftUI.Text {
  public init<Subject>(_ subject: Subject, formatter: Foundation.Formatter) where Subject : Foundation.ReferenceConvertible

Should there be any discouragement of its usage it must be explicitly stated in its documentation.

Without it I fail to see how UICollectionViewCell.updateConfiguration(using:) could be ported to AppKit matching UIKit's behaviour one to one.

I'd take the YAGNI approach: it works now so nothing to worry about, when (and if) it breaks (which may never happen) – worry at that point.

1 Like

That may work if you're developing an app, where there is no public API & ABI to worry about. For a library or a framework, that's a commitment that you can't break without paying the price of releasing a major version of the framework and forcing all users to put in the effort to upgrade.

1 Like

And in this case there is!

Note that even that use is not actually adding a conformance but using it as a constraint. The documentation should definitely make this clearer, but I'm telling you now, the set of automatically-bridged types is not implemented as an open set. Adding conformances to _ObjectiveCBridgeable will break in unpredictable ways, today, even if it seems to work at first. They will break harder in the future.

5 Likes

Ok, these ones then?

public enum INShortcut : Foundation.ReferenceConvertible {
extension PencilKit.PKStroke : Swift._ObjectiveCBridgeable {
extension UIKit.UIListSeparatorConfiguration : Swift._ObjectiveCBridgeable {

If what you are saying is true we should make ReferenceConvertible private ASAP! As with every day passed more and more code uses it. People don't judge the API availability by reviewing obscure Swift forum's threads, they do that viewing the official documentation and there's nothing in that documentation today that warns or discourages users from using this API let alone telling upfront "don't use it, it's for Apple use only".