-
Notifications
You must be signed in to change notification settings - Fork 564
[Improvement] Object and companion object methods should export as class methods/vars in Obj-C/Swift #2757
Comments
See #1549
(Companion) objects are objects. They can implement interfaces etc. So this doesn't seem correct to unconditionally export all object members as class members.
|
Hm I see that’s a good point. It would be nice then to unify the API around getting the shared/companion instance then in Obj-C/Swift: Foo.shared would get the shared instance of Foo object. If Foo were a class, Foo.sharedCompanion would get the shared instance of Foo’s companion object. Thoughts? |
I don't find |
It’s confusing because those 2 methods are supposed to return new instances in Obj-C. But, when used in Kotlin, you’re used a shared instance. Is that not accurate? |
Is this statement applicable to standard frameworks? See my comment above:
|
I agree with you on factory methods themselves. The issue is if you try to do something like NSNumber.maxInt in Kotlin. In Kotlin, I think you would do:
To access this exported to Obj-C, you have to do NSNumberCompanion().maxInt, which feels strange because you’re creating a new companion to access what is essentially static. While I agree there is no keyword that means singleton in Obj-C/Swift, there are well-established patterns in the standard library. See NSUserDefaults and NSFileManager in Foundation or globals in UIKit (UIApplication, UIScreen, etc.). Swift has language-level support for this by making all |
Also I believe the reason that |
You aren't creating a new companion. As I've mentioned above, even standard library itself doesn't follow the convention
Doesn't seem singletons to me, since these classes have custom initializers/factories.
Can hold arbitrary user subclass instance, so isn't quite typical singleton too.
Java has language-level support for this by making all static vars lazily loaded using a clinit under the hood. But there is still a reason behind not exporting all companion object members as Java statics by default. I agree that accessing Kotlin object members is not idiomatic yet, but there is no simple solution for this problem. |
Where is that not the case? The NSNumber scenario is special. That’s either a library or compiler optimization that is non-standard. In general, Swift init returns a new object. Especially with reference counting, it’s supposed to return a +1 retain count object, and the compiler for Obj-C recognizes this naming convention and requires the annotation NS_RETURNS_NOT_RETAINED (even for ARC) if you mean otherwise. I think here we’re okay in Obj-C because the method is just called For the examples I mentioned, I think you’re referring to UIApplicationDelegate, not UIApplication. For the Foundation examples, those are great examples where the singleton naming hints that you can create instances if needed, but there are “default”/“standard” (singleton) instances available for convenience. Whereas UIApplication only uses “shared” in the name, which hints that you shouldn’t create one (I think it also has compiler annotations that also strongly discourage that, but I can’t remember). |
Sorry closed by mistake. |
I get it’s tricky, so I appreciate hashing this out 😊 |
FWIW, I think doing something like Foo.companion (same in Swift, so no init) to access the companion object of Foo and Foo.shared to access Foo, if it were an object would most closely match semantics (along with marking the initializers as unavailable) in Obj-C without going with the full static approach). |
Would making these Obj-C class properties be a compromise? Those would bridge to Swift better. |
What exactly do you mean? |
Oh whoops. That's very vague sorry. I meant to say: would it be possible to make "companion" an Obj-C class property? So |
Technically it is possible and expected to be easy.
|
|
Obj-C class properties were introduced back when Swift 3 was released as a way to improve Swift interop for Obj-C statics/singletons: https://useyourloaf.com/blog/objective-c-class-properties/, so I think it'd be a natural fit here :) |
So ideally: |
You have just confirmed the opposite: there is no common convention on naming: some APIs use |
I didn't mean to confirm a common convention around naming- only that class properties are a popular way to bridge singletons to Swift, which would be nice to see here :) There are some themes in Foundation though:
In this case, SomeObj.companion returning the shared SomeObj.Companion instance seems reasonable and clear. SomeObj.Companion.shared (following the theme explained above) feels overly verbose given the number of characters typed without having even accomplished anything useful yet. |
Swift does have an API design guide, but it doesn't mention singletons specifically: https://swift.org/documentation/api-design-guidelines/. IMO, the best we can do is draw on examples from Foundation and how it has evolved to become friendlier to Swift. |
Sure! But there are also |
Ah right. |
Any update on this feature ? For now, a "workaround for syntax" is to add a temporary extension in your Swift codebase (in case of extension ObjectName {
static let shared = ObjectName()
} It doesn't block you the object initializer, but at least you have a classic Swift syntax when you want to use the singleton everywhere. And if this feature is implemented, you will just need to remove the extension to make it compatible. |
No. |
Shouldn't this issue move to YouTrack? |
The entire issue? I'm not sure about this. |
@benasher44 Is that something you want to do or should I proceed? |
Hi there! This has been one of the more awkward interactions with Kotlin/Native. Let's say we have an
object
:This gets exported to Obj-C as a class has an initializer and an instance method called
foo
, which is strange becausefoo
, when used in Kotlin, is essentially static. We get arround this by writing code like this:It would be great if
Test.foo()
were instead exported to Obj-C such that it could be called like[Test foo]
to match how you'd call it in Kotlin. For companion objects, it gets a bit stranger. Let's say we have a similar setup:In Obj-C, this gets you a class called
TestCompanion
, which you can access by calling[TestCompanion companion]
(funny looking, but it at least matches singleton conventions), but then in Swift it looks like an initTestCompanion()
, which feels odd (init should return a new instance every time, but here I think it returns the static instance). Ideally,companion object
member would be exported as class members in Obj-C/Swift as well.Thoughts on this? This feels like a worthwhile improvement, which would avoid having to frontload a discussion about Kotlin objects and companion objects, if you're just an iOS/macOS developer trying consume a MPP library.
The text was updated successfully, but these errors were encountered: