-
Notifications
You must be signed in to change notification settings - Fork 57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Source and Sink extensions for Apple's NSInputStream and NSOutputStream #174
Conversation
Modeled after JVM's InputStream.asSource() and Source.asInputStream(), add extension functions to similarly map to and from Apple's NSInputStream
@jeffdgr8 thank you for the PR! Unfortunately, I forgot to mention in the contribution guideline that the PR should be created against the |
may be it make sense to also add the same functionality for NSOutputStream in one go? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just minor comments
I'm actually looking into implementing the |
Enforce opening SourceNSInputStream before read
Rename variable
I went ahead and added |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a little more comments/questions :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM after a fix of propertyForKey(NSStreamDataWrittenToMemoryStreamKey)
behaviour
Nice job!
Though, I've not reviewed tests, mostly just scrolled...
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
@OptIn(UnsafeNumber::class) | ||
private class SinkNSOutputStream( | ||
private val sink: Sink | ||
) : NSOutputStream(toMemory = Unit), NSStreamDelegateProtocol { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure how things work under the hood when inheriting NSInputStream/NSOutputStream and calling a non-default initializer (I understand that no-arg initializer is not available here) as according to Apple's docs, these initializers aimed to instantiate some specific stream's subclass.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was the only way I found to appease the requirement Unable to call non-designated initializer as super constructor
, which is the error the compiler throws if you use the no-arg constructor. This seems to be a conflict between the way Kotlin and Objective-C object inheritance works, where Objective-C can return a subclass implementation as self
from an init
method.
I'm not sure there really should be an expected designated initializer in Kotlin for either of these classes. This is likely an interop error. In Objective-C this compiles and I'm able to construct [[MyNSInputStream alloc] init]
just fine:
// MyNSInputStream.h
@interface MyNSInputStream : NSInputStream
- (instancetype)init;
@end
// MyNSInputStream.m
@implementation MyNSInputStream
- (instancetype)init {
self = [super init];
return self;
}
@end
It works to use either the memory or URL constructor of both NSInputStream
and NSOutputStream
. I figured the empty memory versions were likely to be the most lightweight. Is there some way to suppress this designated initializer requirement to call the no-arg constructor? It might also make sense to file an issue to fix the interop. Since we're overriding all of NSInputStream
and NSOutputStream
's public API functions and not using anything from the superclass, I think this should be safe though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Related issues:
https://youtrack.jetbrains.com/issue/KT-47992
JetBrains/kotlin-native#2010
Looks like there is a workaround for disabling in custom .def interop, but this won't work for the platform interop in this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeap, seems like there's no way to inherit streams using no-args init
ctor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In fact, I was mostly concerned about the possibility of inheriting some methods from NSInput/OutputStream subclasses instantiated for initFromData
, initToMemory
. But it's definitely not the case, as there were errors due to non-implemented run-loop-related methods previously (so nothing was inherited).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added the suggested doc comments. It would be strange for a consumer to call any of those init
functions after receiving the instantiated stream object from asNSInputStream()
or asNSOutputStream()
. In Kotlin it already shows an error trying to call init
functions directly, not through a constructor. I don't think I've ever seen a call to an init
function even in Objective-C that wasn't directly after a call to alloc
. Swift doesn't allow this either, where init
functions are only usable to construct objects now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for updating the doc! Yes, it should be possible to call these initializers only from Objective-C code and it seems like nobody actually does that, but it is still possible :')
@jeffdgr8 thank you for fixing all the issues!
I'll try to figure out if anything could be done with the latter (and if there's nothing we can do - I'm fine with how it's done right now). |
Modeled after JVM's
InputStream.asSource()
,Source.asInputStream()
,OutputStream.asSink()
, andSink.asOutputStream()
, add extension functions to similarly map to and from Apple'sNSInputStream
andNSOutputStream
.I previously submitted this PR to Okio. I've been using these extensions, along with my Okio fork, in my KMP library, pending availability in a public release.