It's not a slow process people fear - it's a solution that isn't tailored to macOS/iOS but just another sucking cross platform toolkit nobody wants to use.
Foundation does not seem like a Swifty solution and is not easy portable.
I rather think that it should be available only on macOS and that we (Swift) build our own cross-platform API, being it inspired by Foundation or other languages.
It would resolve issues that we have currently such as SwiftNIO not linking with Foundation because of dependencies.
What is so unswifty at Foundation? I don't understand why it is so hated by non apple users.
The fact that some of it doesn't work on non-Darwin systems?
I recently spent a whole day replacing a CSV library that worked locally but wouldn't in our docker container on CI (and production). The reason it didn't work was that some as NSData?
casts work on macOS/iOS, but they don't on Linux.
This type of bridging has been recently added to Swift Foundation.
I think that people stating that Foundation is not a cross-platform solution are simply saying that because:
- Swift Foundation is not fully completed.
- Foundation on Darwin is using a different Objective-C based implementation.
- Swift Foundation is not at feature parity with Darwin Foundation.
- Ignore the history and evolution of Foundation.
Foundation was designed to be a cross-platform basic framework (the Objective-C version has been working on NeXTStep, Darwin, Windows (I think iTunes is currently using it) and Linux (GNUStep)).
I do think that:
- Completing Swift Foundation should be a priority of the Swift Project.
- The Darwin Foundation Swift SDK overlay and Swift Foundation should be somewhat integrated to allow full cross-platform evolution.
- Foundation design should move to a Swift Evolution style. The design direction and roadmap should be published and updated by the Foundation Core Team.
- Publishing parts of the Objective-C Foundation source code would allow to make Swift Foundation more similar in implementation and performance to it.
- In the long-term it would be great to be able to unify Swift Foundation and Darwin Foundation so all Objective-C entry points would call to the Swift implementation that would be the same in all platforms.
I donât think you understand the evolution process. Direction is given by the core team, who take our reviews in to consideration. Specifically, proposals are not accepted/rejected based on how many +1s they get.
Also, the thing about OOP doesnât make sense. Itâs one specific implementation strategy, which may or may not be the most appropriate one depending on your needs. Class hierarchies are of course supported in Swift, and I havenât heard of any plans to change that.
I trained as an electronic engineer and wrote lots of C programs for microcontrollers (the kind where you have so little RAM they basically donât have a heap worth mentioning, and very little stack). It puts you in a mindset where you want to be really conservative about what you import.
My hope is that one day, I might be able to write Swift code for those platforms. There has really been a lot of development in recent years, and some of them even have C++ compilers (the resulting binary size is roughly the same as C - again, you just have to be careful about using the heap). Right now thatâs a pipe-dream, of course. I donât think the Swift standard library is written with such systems in mind, but as least from an API level it should be portable.
Having the language depend so heavily on a relatively large module like Foundation makes me feel like that will never be possible. Foundation has a lot of functionality which just doesnât make sense on an MCU with no operating system, so I wouldnât port it to those platforms or use it for those projects.
Generally, thatâs fine. 99% of Foundation is stuff an MCU wouldnât care about (URL_Session_s? Processes? Hah). The notable exceptions are âDataâ (which, as I said in a separate thread, I think should be in the standard library), and possibly JSON serialisation.
For the rest, I think Foundation is fine how it is. The APIs could be more swifty and hackable, but thatâs a topic for another day.
Yes, I feel that Data
should be in the Standard Library (as String
and Array
already are).
And, of course, Foundation requires more work to feel Swifty. Two simple examples:
- All the path methods in
FileManager
should allowURL
instead of some allowingString
and othersURL
. -
FileManager.fileExists(atPath:isDirectory:)
should be dead by now and replaced byFileManager.directoryExists(at:)
Simple things like this would probably already been solved if Foundation Evolution was a thing.
It's not a "hate", and not about Foundation, but about Foundation in Swift.
Having to support Foundation and XCTest makes Swift dependent from ObjC, which is a pity.
It is and will limit some evolution on it.
Again issues we have with Foundation/SwiftNIO/URLSession seems like a good example of it.
Swift had an aggressive evolution process not only thanks to the community, but mainly because it had few users and the cost to break code was low. This is not true for Foundation that has many decade of history.
You can't simply drop old methods from Foundation like we did with previous Swift languages features and API.
Apple keeps marking methods as deprecated in Foundation in each new OS release as well as adding new API.
If Foundation Evolution was a thing we wouldn't remove any API from the binary libraries (or event touch the Objective-C version) but could add new API in the Swift overlay / Swift Foundation and mark methods as deprecated.
Also as Swift becomes a language with ABI stability and source compatibility, Swift Evolution cannot be as aggressive as it once was, but that doesn't make Swift Evolution deprecated.
Swift users are not supposed to have experience with Apple platforms and other languages. Not everyone can easily perform the back-and-forth "mental switches" between C-looking apis, Objective-C-looking apis, and Swift-looking apis, that one sometimes experiences when working with Foundation.
This makes me think about the major work that has been performed by Apple on application frameworks (CoreGraphics, UIKit, others), to make them good Swift citizens:
// if (CGRectContainsPoint(rect, point)) { ... }
if rect.contains(point) { ... }
// CGRect rect2 = UIEdgeInsetsInsetRect(rect1, insets)
let rect2 = rect1.inset(by: insets)
I wonder what the same process, applied to Foundation, could do to help users have a better opinion of Foundation. Of course, this would require a great deal of audit, design, and hard work.
I did attempt to play my own game.
Let's look at a Swift 4.2 snippet (adapted from a FAQ sample code):
// Copy a read-only resource into a place where it can be modified.
// Perform this copy only once.
let fileManager = FileManager.default
let path = try fileManager
.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
.appendingPathComponent("file.extension")
.path
if !fileManager.fileExists(atPath: path) {
let resourcePath = Bundle.main.path(forResource: "file", ofType: "extension")!
try fileManager.copyItem(atPath: resourcePath, toPath: path)
}
doStuff(path: path)
It does a lot:
- Look for a standard directory... (with support for Apple platforms, but not only)
- ... in user's domain (I lack the skills to know if domains are cross-platform, or purely useful for macOS server and its NeXT ancestors)
- URL manipulation
- URL to path conversions
- Check for file existence (without error handling)
- Load a resource's url from a bundle (I don't know if "bundle" is a portable concept, but I guess "resource" is)
- copy files (or directories)
What if I would attempt to "modernize" it?
let fileManager = FileManager.default
let url = try fileManager
.url(for: .applicationSupportDirectory, inDomain: .user, options: .createIfNeeded)
.appending("file.extension")
if try !fileManager.fileExists(at: url) {
let resourceURL = Bundle.main.url(forResource: "file.extension")!
try fileManager.copy(resourceURL, to: url)
}
doStuff(url: url)
- I've kept FileManager. This beast does a lot. I'm not there to throw the baby out with the bathwater.
- I've kept the notion of standard urls.
- I've kept the notion of domains, because I dare not removing them.
- Replaced the
create: true
boolean argument withoptions: .createIfNeeded
- Simplified
appendingPathComponent
toappending
. But I guess this change is ill-advised, because you are not supposed to append strings with forward slashes, here. - Never converted urls to paths
- Made FileManager.fileExists throwing
- Made resource url extraction legible
- Compacted FileManager.copy(_:at:)
I'm not that sure that it is much better looking, as a matter of fact.
No disagreements that FileManager is a beast that does a lot (more than I even understand), but I do think it should be replaced . Even your updated version doesn't feel very swifty to me. It lacks a lot of the strongly typed features that so much of the language has in other areas.
Paths shouldn't just be generic strings/urls (I'm ok with the underlying storage being a string/url). There should be separate types for the various path types (ie: DirectoryPath, FilePath, etc) because each path type has different sets of functionality. Rather than interacting with paths via FileManager, I'd prefer to be able to do everything on a Path directly. For example, you shouldn't be able to incidentally try getting the subpaths of a FilePath, this is something that should be limited to DirectoryPaths.
Converting your example to what my ideal Swift Path library would be:
let supportDir = DirectoryPath.applicationSupportDirectory(inDomain: .user, options: .createIfNeeded)
let newFile: FilePath = supportDir + "file.extension"
if !newFile.exists {
let resource: FilePath = Bundle.main.file(forResource: "file.extension")
try resource.copy(to: newFile)
}
doStuff(path: newFile)
Of course, I'm not very familiar with Bundle
nor do I have any idea what the FileManager domains stuff is.
As a server-side developer, I don't actually know what "application support directories" and "bundles" are, so I suspect these are not strictly portable concepts. :) As such, they could be maybe part of a library separate from one that does just basic file I/O.
Open question: is it a good idea to let paths access the file system? There is a global state there.
The current API, where all accesses to the file system are done via a FileManager instance, avoids this problem. You can write tests against a virtual file system, for example.
I was thinking the same thing. Much of FileManager is Apple/iOS specific and would probably not be necessary for a potential stdlib Path library. The Apple/iOS specific stuff could be kept in Foundation and just extend the stdlib Path types with support for Bundles and whatever else is needed.
No, that would only lead to apple api being a special case and not the norm. Why should I as a mac coder support such a development? For what gain? That Linux guys are happy?
Looking around at some of the Swift Path libraries out there, they're almost all just wrappers around FileManager
, but they seem to only really focus on giving more swifty access to the local file system. (Some do allow you to overwrite the FileManager used though)
You do bring up a good point. There probably should be functionality for testing against virtual file systems.
Perhaps by overwriting the root directory with a virtual one:
Path.root = DirectoryPath("/").virtual
Swift is supposed to be a general purpose programming language. You may disagree with that, and maybe this is more an aspiration than current reality, but that's what I understand it to be. As such, yes, Apple would be a "special case" and not the normâjust as any other platform. I don't see why this should be a bad thing, nobody is saying there shouldn't be good, useful libraries for working on Apple platforms, just that there should be a separation of concerns so the non-platform-specific stuff is reusable. ÂŻ_(ă)_/ÂŻ