FoundationNetworking

Of course, never will be for dependencies. The problem here is that we are creating a burden an a cross-platform inconsistency.

What about adding a environment variable so clients which would want to block FoundationNetworking from lazy-loading can do it while for the rest of the programs could keep using String(contentsOf:)...?

For example, NIO or whatever library can set that environment variable on its initialization and then Foundation would refuse to lazy-load FoundationNetworking and crash instead.

1 Like

The other day I found an instance that a "invalid" URL was crashing because of the FoundationNetworking code path.

let url = URL(string: "bogus")
let data = Data(contentsOf: url)

The intention of the code in a test was actually not loading anything (nil data), but when I upgraded to the current Foundation, I got a crash instead. This was intentional, but this might had happened because of an error in processing user input or similar. Where before we got a nil, that the code must handle, now we are at the risk of crashing, without an easy way of checking first.

While I think that the split of Foundation and FoundationNetworking is great, maybe the current code can be improved. The above snippet is obviously not a remote URL, but it was crashing because the check is too narrow. I don't, however, know what the exact check should be.

Another detail that was a little unhelpful was finding the exact point where the Data(contentsOf:) was being invoked. I only had the textual information of the fatalError, which sadly was pointing to the file/line in the Foundation code. It was also not telling me about which URL have failed. The later can be easily fixed, the former would be impossible to fix unless every public API that leads to the loader code accepts file/line information, which I think would not be accepted as a PR at all.

As I say above, I'm all about the split, specially if it means that we can see a SwiftNIO backed FoundationNetworking, but I can see the pain that it causes to the ones using those initializers.

1 Like

Good point. I think we should then enumerate schemes in Foundation that the FoundationNetworking implementation is known for implementing rather than "assert file://". This way you only get the message specifically for HTTP, FTP, etc. URLs.

The fatalError has a pretty specific error message; was it lost? A lot of our error machinery is based upon the fact you can get a backtrace, but on Linux that seems to be widely unavailable — I can get them easily while I work on Foundation because most of the time I can reproduce in a debugger, but it doesn't seem to be a tool available to most developers here. Is there a way to add this context to your debugging arsenal on Linux that I missed? I know Ubuntu has a crash reporting facility that includes backtraces, but I'm not sure how it integrates with the Docker images.

(Also, generally speaking: on Darwin, os_log can automatically filter information that should be private. Since we don't have access to os_log in s-c-f, I've been wary of printing parameter information in defaults-to-on logging or errors that may cause privacy-breaking leaks in logs.)

The backtrace is a little finicky in Android. I introduced some changes not long ago to get the addresses, but the symbols are hard to obtain. Saleem had some code to do it, but I haven't had time to see how to integrate it. I remember needing a switch to make it work in stdlib, so maybe CF also need the same switch, to be able to walk the stack trace across CF. I will have to have a look at that at some point. I just thought that the error would be more informative if it pointed to the invocation point in the user code, not the library code.

And you are right about including maybe-private information in the logs. An URL is not a big deal, but it might include such information. I didn't think about that point.

For anyone else landing here after searching Google for an error message, the very first post has a disclosure triangle hiding the answer to: How can I get the same source code to build for macOS and Linux?

#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

8 Likes

I wanted to hear from you — is this going to affect your project, and to what degree?

A small deal, I noticed my project failed to build because of this and the one or two

 error: 'URLSession' is unavailable: This type has moved to the FoundationNetworking module.

lines were swamped out by a hundred

error: use of undeclared type 'URLRequest'

which didn't have that helpful suggestion, which would have reduced my confusion quite a bit. Should this annotation be added to URLRequests/URLResponses as well?

Somehow when using String(contentsOf: url) on Linux this only throws: Error Domain=NSURLErrorDomain Code=-1 "(null)" while on macOS it will load the content properly.
Is there a way to fix that?

It looks like FoundationNetworking is loaded in your process, but there’s an issue with it. Can you add your code to bugs.swift.org for a bug report?