Are Linux and Darwin Foundation meant to have the same API?

I've been trying to turn a library I use for internal testing that mocks URLSession in test to be an open-source SwiftPM project that works on Linux. The library has been used without significant issue on iOS. No good deed goes un-punished.

There are a number of issues that have come up in the port. But one class of problem has me wondering are these bugs or "intended design"?

For example, I'm getting the following compile error on Linux:

/vhs/Sources/VHS/Types/VCRTask.swift:100:18: error: cannot override mutable property with read-only property 'state'
    override var state: URLSessionTask.State {
                 ^
Foundation.URLSessionTask:27:14: note: attempt to override property here
    open var state: Foundation.URLSessionTask.State { get set }

If I understand things correctly Foundation on Apple's docs say state is read-only. However, that compile error seems to indicate that Foundation on Linux has a different API. Indeed, looking at the code seems to corroborate that.

There are others like this. Put simply is the Foundation provided on Linux meant to have the same API as the Darwin platforms?

1 Like

you're learning now what i learned as a linux swift dev a few years ago: don't import Foundation

4 Likes

Sadly I am aware. Though to be fair, I'm the blocker on those Foundation issues. I probably bit off way more than I could chew with either of them.

Fixing some incongruent API declarations seem like something I could fix, if indeed they are desired to be fixed.

1 Like

Yes, the desire is to have the APIs be congruent AFAIK.

CC: @millenomi @Tony_Parker

2 Likes

We have been working tirelessly to bring API to parity in this last release. There have been already some minor fixes on URLSession in the last couple weeks, and you should expect more as we get closer to the release date.

The desire is for the cross-platform portions of Foundation to match each other. You can find the bulk of the work in SR-10347, and any discrepancy is a bug that I want to fix.

2 Likes

Also generally speaking URLSessionTask isn't subclassable in any meaningful way; what are you trying to do?

Providing a mocking library for URLSession.

Record your test suite's HTTP interactions and replay them during future test runs for fast, deterministic, accurate tests.

Or that is the goal at least. The record part still isn’t working. It doesn’t support all of the different types of tasks. But for what we’ve using it for, replicating headers and data, it works. Doesn’t replace integration tests.

I want to note that while the idea is interesting, you can't not invoke at least some Apple code when you do this, because your initializer has to invoke a superclass initializer, so the solution is brittle in the long term.

I feel like I'm veering into off-topic for the original question. But my interest is piqued because for a few of our iOS apps we've really started to rely heavily on this framework for testing.

I'm not sure I know how to heed the warning because I'm not sure that I understand the warning.

... you can't not invoke at least some Apple code when you do this, because your initializer has to invoke a superclass initializer ...

Our goal is to not implement any Apple code. Our goal has been to basically "plaster over" all the Apple interfaces and effectively treat URLSession as a protocol. I think in most cases we end up using, bordering on abusing, a combination of private underscored prefixed variables and override to make it work. Can you expand on what I should be careful of?

I think we always new it would be brittle considering it is a library that is meant to provide "similar" behavior to a closed source library that we implement by "testing the fences" (Jurrasic Park :t_rex: Muldoon voice). That in and of itself does not really shock/scare me. It is as clunky as you imagine and I hate every minute I look at it. But the part that makes me stomach it is all the clunky is encapsulated in the single dependency that I don't have to repeat across projects. More over it is a single test dependency that does not ship in my production code.

Without URLSession implementing some protocol I'm not really sure what else to do. As an aside, if you know someone who could make that change to Foundation :pray:.

One early iteration of this library we made a protocol in VHS and then made URLSession conform to that. The problem is then the VHS library was a run-time dependency not a test-time dependency :face_vomiting:. We also prototyped the protocol in the application and then making VHS conform to the protocol :nauseated_face:. Admittedly, this was way less offensive because VHS could remain a test dependency. But now we had this protocol that lived in our application that was ostensibly only for testing. Either way it felt like we were violating separation of concerns. In the end I liked encapsulating the clunky in a dependency that I could eject in the future when a better solution arose.

I'd really love any feedback that could help me prevent long-term headache.

Two things:

  • What I meant is that you can’t paper over all Apple code if you subclass an Apple subclass. The initializer calling super example is relevant here because URLSessionTask subclasses aren’t meant to be initialized by anyone other than the framework, and if the class starts enforcing it on Darwin we will make it occur here as well. This may break your code.

  • You can always add a conformance to a protocol you create to any class, including URLSessionTask and its subclasses.

and if the class starts enforcing it on Darwin we will make it occur here as well.

Well that did not take long. It's almost as if you had inside information. :laughing:

4 Likes

What tool is that screenshot from?

https://developer.apple.com/documentation/foundation/urlsession?changes=latest_minor

1 Like

Thanks! I’ve never noticed that view before. Useful.