Idiomatic (nested) namespacing strategy

As far as I am aware, Swift lacks the namespace keyword and the ability to explicitly define namespaces. However, one can prefix imported functions and types with the module name they were exported from. There is a workaround using enums or structs together with static functions, but when using these, one has to use fully qualified names, such as MyNamespace1.MyNamespace2.myFunction, which can sometimes be cumbersome. Another workaround involves using typealias.

The question is: what are the best practices regarding this?
Should one simply avoid nested namespaces? Or are they acceptable, and developers should just get used to them? Alternatively, should one define appropriate typealias for convenience? If typealias is considered idiomatic in this case, where should it be defined—within the library itself or in the client code?

Thanks.

P.S.: If there are any factual errors, I would be happy to be corrected.

1 Like

My approach for this (as far as libraries go) is usually to define a single top-level enum, and nest (via extensions) types in that enum that depend on other types in that namespace.

enum Vehicles { }

extension Vehicles {
    struct Sedan { }
    struct Pickup { }
}

That way I avoid having to carry typealiases around everywhere. It's not perfect, especially since extended types don't inherit the parent "scope":

extension Vehicles.Sedan {
    typealias OtherCar = Pickup // doesn't work
}

Anyway, in short, I'm not sure if there are best practices — I'm curious what other input or suggestions others have.

1 Like

I think whenever enum or struct is used for anything other than defining enumerations and structures, that's already not idiomatic in Swift.

Sometimes nested types come "naturally", e.g. you may have something like List.Item. But other than that, I don't think there is a nice and let alone idiomatic way to have nested namespaces.

Could you elaborate, how is typealias an alternative to nested namespaces?

1 Like

It's similar to C++ using std::cout;

After you do it - you can type just cout instead of std::cout

For example if you have MyModule.MyEnum.MyType you can do something like typealias MyType = MyModule.MyEnum.MyType and use MyType without preceeding MyModule.MyEnum

That works for types, importing methods is a bit quirkier, I believe that if you don't care about the named arguments you can just do let myFunc = Namespace.myFunc or define another function that wraps the one you want, but I admit that it is cumbersome to use.

1 Like

From my experience it's almost never necessary in Swift. Firstly, the module qualifier/prefix is optional. You use it only when there are naming conflicts between modules that you use in the same Swift source file, and you need to disambiguate. So I'd say it's opposite to C++ in a sense that namespaces are not forced but can be disambiguated when necessary.

As for nested types, like MyEnum.MyType, this is not very common, library creators don't do this as far as I can tell, or at least not just for the sake of having a namespace (apart from "natural" cases of nesting that I mentioned before). You will likely force your users to use type aliases anyway, so why bother?

Sometimes you want to group together some global functions under a single umbrella. I usually do it using a class. The naming would be such that the class name and the function names complement each other. For example, instead of

func analyticsLogEvent(_ name: String) { ... }

I would define it as:

class Analytics {
    static func log(_ name: String) { ... }
}

This way, a call to Analytics.log() becomes self explanatory without any redundancies, so there is no need to define an alias.

1 Like