Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper namespacing (or class naming) convention in Swift frameworks (for iOS)

TLDR

  1. If framework is called AEConicalGradient, should classes in it be called ConicalGradientLayer and ConicalGradientView or just Layer and View?

  2. If framework is called AELog, and it has class also called AELog and other class called Line with some delegate callback which is using this Line type, how to implement this delegate if your code also has defined type called Line, because adding framework name as a prefix in method signature func didLog(line: AELog.Line) will make error 'Line' is not a member type of 'AELog' which in fact it isn't (it's part of AELog framework, but it's not member of AELog class inside of it).

Read More

While updating several frameworks of mine for Swift 3 I was having a lot of second thoughts about what's the proper naming convention for framework classes depending on framework name itself.

In the end I realized that I didn't really used same convention across different frameworks, so now I don't like this lack of consistency.

I'll jump straight into some real-world examples which I encountered:

1. Example: Framework doesn't have a class named as framework name:

Framework is named AEConicalGradient and it consists of 2 public classes:

  • CALayer subclass
  • UIView subclass

What should be proper name for each of these classes?

a) AEConicalGradientLayer and AEConicalGradientView

b) ConicalGradientLayer and ConicalGradientView

c) Layer and View

Discussion:

a) I used this in the first version of framework with both of these classes in single AEConicalGradient.swift file. I don't like this approach anymore, I'm separating sources into multiple files so now I'm thinking between b) and c) approaches.

b) In this approach I like the fact that someone can just drag these files into project (e.g. without using any dependency manager) or copy/paste code and it wouldn't loose context too much (because of clear names), but on the other side it sounds a little bit repetitive if you think of it as AEConicalGradient.ConicalGradientLayer and AEConicalGradient.ConicalGradientView.

c) AEConicalGradient.Layer and AEConicalGradient.View sounds like the way I want it, but on the other side those are more likely to make a conflict if someone has their own Layer or View classes, or if they just drag files or copy/paste code context is pretty much lost (like what is this Layer or View class for?).

2. Example: Framework does have a class named as framework name:

Framework is named AELog and it consists of 3 public classes and a delegate protocol:

  • Shared Instance (and delegate for it)
  • Log Line model
  • Config

Discussion:

a) Similar to the first example in the first version of framework I had classes named AELog, AELogDelegate, AELogLine and AELogConfig - all inside single AELog.swift file.

b) In the latest update for Swift 3 I ended up with multiple files and class names: AELog, AELogDelegate, Line and Config.

That's when I realized that if you import AELog and implement AELogDelegate (which has only func didLog(line: Line)) AND you already have a different Line class defined in your own module it will make a conflict which can be solved only by renaming one of those two Line classes!

To be honest, I don't really understand how to avoid this. Because if you try to namespace type in delegate method like func didLog(line: AELog.Line) you'll get error 'Line' is not a member type of 'AELog' which in fact it isn't (it's part of AELog framework, but it's not member of AELog class inside of it).

That's when I started to feel a bit concerned on this "loose" naming. Any suggestions or opinions on this specific matter?

c) In the spirit of very "loose" naming like in 1.c) approach, is it ok to rename AELog class to just Log, and AELogDelegate to just Delegate? Would this fix error stated in 2.b) because there's no class named same as framework name so maybe compiler would recognize AELog.Line in that case?

Question:
What are some "clean code" arguments on either of these approaches? Is there an obvious "proper" way on how it should be done? Am I missing something important here?

like image 630
tadija Avatar asked Oct 24 '16 22:10

tadija


People also ask

Is there namespace in Swift?

Even though Swift doesn't support namespaces within modules, there are a few viable solutions to this problem. The first solution uses structs to create namespaces and it looks something like this. We define a struct, API , and declare one or more static constant properties.

What convention is used in naming classes?

Class names should be nouns, in mixed case with the first letter of each internal word capitalized. Try to keep your class names simple and descriptive. Use whole words-avoid acronyms and abbreviations (unless the abbreviation is much more widely used than the long form, such as URL or HTML).

How do you name a swift protocol?

Protocols that describe what something is should read as nouns (e.g. Collection ). Protocols that describe a capability should be named using the suffixes able , ible , or ing (e.g. Equatable , ProgressReporting ). The names of other types, properties, variables, and constants should read as nouns.


1 Answers

TL;DR

  1. The type names should be ConicalGradientView and ConicalGradientLayer. "View" and "Layer" are just not descriptive enough for the case of a "view" or a "layer" concept.

  2. It is not possible for the Swift compiler to disambiguate a clashing module / type name in that context. You will not find code from Apple or from 3rd parties that use clashing names for frameworks / modules vs types within them. This may be for a technical reason, but it also would be confusing and your case of 'AELog' as a Swift type name is also not idiomatic.

The long answer

The name of a type should somehow describe its role in the program you use the type in. Whilst this is of course subjective, I don't think a "View" is very descriptive name for a view subclass, given there are dozens of NSView / UIView subclasses in Cocoa alone and views fulfil potentially hundreds of roles across your own app. So, by the logic that just by seeing that unqualified type name alone you can't really make guesses about what it does, therefore it cannot be a good name. The module / namespace name's function is more to provide a minimal level of disambiguation that leads to undefined behaviour on name clashes (a luxury not available traditionally in C / Objective-C for example), but arguably its role is not to be routinely used in the fully qualified form.

Think about it also from a perspective of "what if I wanted to introduce a new variant of a View in my framework?" – a separate framework for specifically conical gradients for instance sounds a little bit funny. A framework perhaps for different sorts of views that have a gradient to them sounds like a more reasonable "minimal size" for a framework (even if you may have implemented only a conical case for your needs for now), and the same applies also to a concept like "Layer". If "AEGradientView" for instance is the name of your framework, then you could have things like ConicalGradientView, RadialGradientView, etc in it, and that would read out all nicely. Finally, should you wish to make things more brief when consuming APIs from that framework, you can always use typealias.

So, being sufficiently descriptive is one factor, but of course brevity is the other factor you want to balance. If you really genuinely do have a case of a type that really is unique in the framework and the type name can be both brief and clearly indicating its role, then my convention at least is indeed to use the most brief possible name. So for instance in a certain image data and metadata handling framework which I've co-designed, Image is the name of a type used as a container for image data and metadata. because there genuinely in the lingo of that framework only ever a single class in an "image" role, and crucially, it describes clearly what the class is for (unlike for instance "View"). In the same way, I use the briefest possible name for nested types, so for example the type used to denote an "image error" (an enum conforming to Swift.Error) is called Carpaccio.Image.Error.

Regarding your second question about a module / type name clash, this ambiguity is not possible to resolve by the compiler in that context. You also indeed will not find any instances of a module and type name getting confused in any of Apple's frameworks (nor can I think of having come across any example of this in 3rd party code). Beside the technical reason that the compiler most likely has no means of disambiguating between the module and the clashing class name there, it's also confusing for a programmer following platform conventions: think of the module name as the name of a "product" and the types inside as the constituent parts – whilst AELog is a good name for a logging framework in Swift, it would not be an idiomatic name of a type in Swift code; Name prefixes like "AE" are just not used in Swift for types – they're not even necessary for Objective-C subclasses you make in Swift because the fully qualified name of the class seen by the Objective-C runtime actually includes the module name as a prefix.

There's a nice WWDC talk from earlier this year also on Swift API naming considerations, discussing some of what is mentioned above as well.

like image 52
mz2 Avatar answered Oct 03 '22 22:10

mz2