Best practices for declaring variables

I have completed my first mini-app with swift on iOS, it 's working, but I'm wondering whether I followed best practices in declaring entities that need to be used in multiple views (in SwiftUI, using Xcode ).
So far I have : global constants, global variables, observable objects, extensions (for example Color extension for customized color themes)

Should I put them in one dedicated swift file, in the Home Controller View, which handles everything, within the files that defines data models, especially the one that loads json data ?
Does it matter ?

So far I have all of them in a dedicated initialisation file, and created separate files for defining the observable object classes.

Also it's not very clear to me whether I should use an observable object type or a simple class within a view when refreshing of the view is not needed, or controlled by another observed object

Thanks for your advice

1 Like

I would

  • put observable objects in the same file as the object that will mutate them the most,
  • put global constants in the same misc.swift file, except if the constant appears in a single file,
  • avoid global variables,
  • put extension based on how much extension needs to know, even if it extends Foo, should it use a lot of Bar, I'd put it with Bar.

I tend to maximize the amount of private/fileprivate. It ends up being surprisingly easy to reason/organise.

Thanks for your quick answer.
Could you put me some rationale for it ?
I guess your answers imply the real emplacement does not logically matter, it is rather a matter of convenience, which may, then, may be an implicit opinion about most frequent usage.

  • put observable objects in the same file as the object that will mutate them the most,
    => I guess to have it at hand to change default variables, but then, if there is a init performed, why would be the use ?

  • put global constants in the same misc.swift file, except if the constant appears in a single file,
    => I would prefer to know that all my constants are in one place if I come back in one month, rather than to go after each file, no ?

  • avoid global variables,
    => why, and then use what ?

  • put extension based on how much extension needs to know, even if it extends Foo , should it use a lot of Bar , I'd put it with Bar .
    => that is not very clear to me, in the case where the extension is only defining a constant (for example, a new color)

  • I tend to maximize the amount of private / fileprivate . It ends up being surprisingly easy to reason/organise.
    =>maybe I should read some documentation again, as I'm not sure why it helps

You can "jump to definition" quickly, which generally works well. So the placement doesn't matter too much. Even if it proves troublesome, I don't find "finding Codable extension in Foo.swift" much different from "finding Foo extension in Codable.swift".

All the suggestion above are for maximizing private/fileprivate. I tend to use file as a mini-module. It does help that you don't cross the file boundary too much. If I want to find private Foo.property, I don't need to switch file, and can find all occurrences quickly. Also, private identifiers don't collide with ones in other files, allowing you to use generic names like height, width, etc. You can even use simple "search"/"replace" instead of sophisticated "refactor" which I see as a neat plus.

Most of the time there are only a few objects that mutates an observable. The rest will just read. The write portion generally need some private access to observable object, that's why I put it there.

If there are multiple writers though, I'll just put it in a separate file.

Perhaps, hence the misc.swift. Then there is this exception that you generally want to know about some constants when you see them being used. So the single-file exception is kind of striking that balance for me. You can even use generic name (with private of course) which would collide otherwise.

In one project, I use multiple color pickers, each with its own colorSpace. I could do rgbPickerColorSpace, labPickerColorSpace, etc., but putting it at the top of the file and name them colorspace is much more concise. I find myself doing this a lot.

Global vars can be very troublesome, you already assume a few things about them, that it's the same application-wide, accessing it should be thread safe, just like singletons. At the same time, it doesn't look the part, it looks just like normal local variable and it can throw you off your reasoning at a glance at the code. I tend to wrap it in a namespace (empty enum), or use singleton altogether.

Though often time, it can be refactored into some top-level values, like EnvironmentValues or EnvironmentObject from the top-view.

Then the placement doesn't really matter to me. I'd treat it as a namespaced constant, and put in the same file as the original class/struct. Sometimes extension uses properties of other object (extending Foo, but implementation relies heavily on accessing Bar), that's what I'm talking about.

Thanks again.
Excuse me if I'm a little slow but I'm not a professional programmer and English is not my first language.

My understanding from your answer is that :

  • indeed the position of the variable does not logically matter for the compiler
  • but it can lead the programmer to make mistakes if not properly done
  • you favor placing the variable where it is more convenient to write the code and deal with bugs/ optimization
  • whereas I was implicitly thinking that now the code works, maybe I should put it in a logical place. Now, I see my rational is flawed if I want to to rewrite some part of the code, which cannot be ruled out.

Thanks for clarifying the importance of writing concise variables and advice about using environment values for constants. Also for using in-file "find", which I tended to overlook.
However I did not get your point on global variables : " I tend to wrap it in a namespace (empty enum )"

Not sure what logically matter for the compiler means. IIRC, the compiler does process each file separately, though I wouldn't be too concerned about it.

I actually organize them like this to reduce bug that I may create. I can see everything I need without switching between files–something I find distracting. So chances are, I'll know what I need to be aware of.

The item placement usually is more about convention than anything else, all for the sake of knowing where things are. So I believe you'd develop one over time. I just share my own way of doing it, though you could easily find one that works for you.

Something like this

enum Namespace {
  static var x = 3
}

So whenever you refer to it, you'd do Namespace.x. It is clear enough for me than just x.

1 Like

Thanks for all your answers.