Namespacing

I've just started using Swift (long time dev though) and I'm trying to figure out the best path forwards for organising a codebase. From what I've read namespaces don't really exist in Swift and people resort to things like using enums to wrap things up. Has the community reached any concensus on the best approach? The approaches I've seen are

  1. Use enums - main downside appears to be that everything inside an enum must be in the same file, so if you've got multiple classes inside a namespace you're going to end up with an awfully large file.

  2. Use structs - these appear to be more flexible, you can declare them once and then use extensions to add classes/structs etc. in different files. Main downside is that you can create structs which is a bit weird for something that is actually a namespace - could probably live with it.

  3. Use a flat naming structure, instead of Projects.User just use ProjectUser, feels pretty clunky if you had to use those names in code but perhaps aliases would allow to shorten them at the top of a file (not sure whether you can alias all of types/structs/enums)

  4. Use separate projects for each module in your app. It seems a little heavy handed to break out new projects for every module but perhaps this works well in practice?

  5. Something else?

So what do you use? If you were starting a new project today which approach would you use?

4 Likes

Have you considered using multiple Swift modules in the same package? Modules also act as a namespace.

2 Likes

I'm not entirely clear what you mean by a Module, am I correct in thinking that Modules are aligned with Projects?

A swift module is a unit of code distribution: a set of implementations and interfaces - e.g. a library or executable.

I guess you are in the XCode > New Project world of Swift? In that case yes, by default you would get one module per application or framework project.

You may want to take a look into Swift Package Manager. Swift packages are allowed to have multiple modules, so you can keep everything in the same project and still use modules as a unit of code organization.

I don't understand what this means? Can't you extend an enum in the same way you can extend a struct?

1 Like

For what it's worth, recent frameworks (e.g. Combine) have been using enums as namespaces (e.g. Publishers). You can indeed extend enums in different files.

I used to be in the #2 camp (prefer structs) and would just make a private init() {} to prevent creating an instance of the namespace. I've since been converted to camp #1 (prefer enums.)

Apple frameworks have been updated over time to reduce the amount of flat names in the global namespace. It has somewhat reduced discoverability but in the same stroke has decluttered the global namespace. I think, for the most part, this has been a great thing.

6 Likes

All nominal types in Swift can be extended exactly the same. Structs aren’t any more or less flexible than enums in that regard. Enums are sum types, structs are product types. That’s the only difference.

1 Like

You may want to take a look into Swift Package Manager. Swift packages are allowed to have multiple modules, so you can keep everything in the same project and still use modules as a unit of code organization.

Ah I was advised to go for cocoapods but this does sounds interesting. That said I haven't been able to find any information about this (maybe I'm googling the wrong thing), could you point me in the direction of some docs or a blog?

edit: Found this post which was a good introduction https://nativeconnect.app/blog/sharing-modules-using-swift-package-manager/

1 Like

I read this somewhere but you're right, enums can be extended just the same as structs, IndianredGrubbyMisrac - Swift Repl - Replit.

Thanks for all the replies, they've been very helpful. I've decided to start off with using enums as namespaces and then spin them out into swift modules when the boundaries become clearer.

2 Likes

I've been using enums for quite a while and it still looks like an awful hack. What I would love is a namespace feature that has the ability to define a submodule. So the internal access is only accessible within the namespace and you need to use public or open to be able to access outside. Why not just use a framework? Honestly, because frameworks are heavy handed. They require maintenance, separate targets, etc... and they are not as fast as direct linking. And they are overkill when I have 5-10 related types that provide a subsystem.

Maybe there is a non-framework way to have multiple related types access each other while still protecting their internals with a public interface (without having to have them all be inside the same file)?

2 Likes

I think multiple modules within a single Swift package is a fairly elegant solution to this. It ends up speeding up build time, since typically you are only building one or two modules at a time.

It is a little bit of a pain to set up the module structure in the filesystem since it requires some manual steps, but I am working on a tool to automate this which I hope to release in the next couple of weeks.

2 Likes

That tool sounds very interesting

Doesn't that have possible negative performance implications? How good is the current Swift compiler with cross-module optimization?

I don't know the metrics about whether this strategy achieves parity in terms of performance with a single module, but it is possible to avoid the major performance penalties. I.e. you can avoid witness table lookups on generic function using @_specialize or @inlinable. This has gotten better since the introduction of .swiftinterface files.

This seems like a good idea on paper. However to my knowledge (please correct me if I'm wrong, this would be awesome news :grin: ) each module will still produce its own framework and which sadly leads to a painfully long launch time on your iOS application when frameworks start to stack up ...

I think they improved the starting load time significantly with iOS 13 (or 12)

I don't know. Usually I am not using Swift to target iOS, and when building a with SPM generally everything is just linked into one static binary so this isn't an issue.

This isn't really true with (pure) Swift frameworks, as they don't have to pay the startup costs that Obj-C frameworks do. The slowest part of framework startup is usually Obj-C registration and +initialize implementations.

@filiplazov Indeed I think it was mentioned it in the last WWDC
@Jon_Shier That's an interesting point ! Is this documented somewhere by any chance?
This is all interesting because increased launch time is the only thing that holds me back when I want to export code in separate framework. Back in 2016 Apple suggested that we should not have more than 6 frameworks and I ran into issues where an app took more than 10 seconds to launch (it had around 20-30 frameworks) :sweat_smile:. But it was some time ago and I should definitely make some performance check with pure Swift modules.