Open jpmcglone opened 9 years ago
I really enjoyed reading your comprehensive walkthrough of the suggested project directory organization. Nice job :+1:
Actually, I'm using similar context-oriented directory structure in some projects and it's working well. I agree that it feels more natural to group files together by their context rather than by their kind. At least for me, it's simpler to work with the files in Xcode (or AppCode) when the files that are being worked on are kept closer to each other. It eliminates buch of mouse clicking and opening/closing groups in the IDE. Also it helps to separate the concerns from each other a bit more clearly.
Though I do find that, for instance, if a view is used in several contexts that putting it in a group called Common
to be a bit awkward. Mainly because it's a little bit more difficult to reason where the view is used. But that's inevitably and quite minor inconvenience that I've noticed in practice. :partly_sunny:
I'm in favor of adding this project directory organization to the document. To me, it's better practice than the structure we're currently promoting in the document. @richeterre What do you think?
Thanks for the reply!
Yea, I'm browsing some of these 'best practice' repos and searching blogs and things because I really want to make one of my own. I see a lot of projects doing what this Best Practices repo suggests (views, models, controllers as top-level folders), and I've just had better experience the other way.
But I must say, I love this guide overall. :D Lots of great tips, especially for people just getting into building iOS apps w/ a team.
Haha @jpmcglone, amazing timing! I only just the other day had a heated debate with my dear colleague @richeterre about this, me arguing for a more context-based organization like the one you suggested. You provide some great ammunition here.
I agree that the most problematic aspect are the reusable files, but your solution here seems like a workable compromise. We're also looking at moving to a more VIPER-like architecture in our current project, which would mean that each feature (or context, or module) could contain all of:
FeatureView FeatureViewController FeaturePresenter FeatureInteractor FeatureDataManager (still working on a better name for this component)
In this scenario, context-based tree organisation makes even more sense.
(I have a sneaky suspicion that the main reason I got into heavily relying on Cmd-Shift-O for finding files, is that our organisational scheme was lacking.)
First off, a huge thanks to @jpmcglone for your input! I agree that when you create feature subgroups anyway, our currently proposed structure doesn't make sense. However, in some projects we have simply grouped all views (or view models, helpers, …) without further subgrouping. We then linked these groups to similarly named folders on disk, to avoid having all files at the same level. How do you approach this?
In any case, it looks like we're now actually going to try your suggestion in a project and update the document with our findings accordingly :+1:
@richeterre Ah, so just 1 layer deep. That's interesting! I guess that does simplify it a bit (compared to what I described)
And hey, it's really cool that you're going to give it a try. I'm looking forward to your reporting back :D I do all my projects like this, and I find merging in multiple branches without merge conflict alone is a great feeling. I hope it works out for you!
I agree with this - this structure is common for large applications in other languages as well. where would you recommend protocols go in this file structure?
@jpmcglone I totally agree with your organization, but I do have a question, how do you organize your folders when there are multiple features, and some that are nested?
What I mean is, let's say we have this (forget about Models, assets and everything else for now, let's consider just the presentation layer):
├─ Application
├─ Main
├─ AppDelegate.swift
├─ MainViewController.swift // This is a UITabBarController
├─ Login // This is a modal that might be presented by the MainViewController (if user not logged in)
├─ LoginManager.swift
├─ LoginViewController.swift
├─ LoginHeaderView.swift
├─ Feed // This is tab 1
├─ FeedViewController.swift
├─ FeedTableViewCell.swift
├─ Friends // This is tab 2
├─ FriendsViewController.swift
├─ FriendsTableViewCell.swift
But now let's say that I have a FriendDetailsViewController
that can be reached when I tap on cell in the FriendsViewController
.
Should the FriendDetails
feature be next to Main/Login/Feed/Friends
, or should it be inside Friends
?
What about Login/Feed/Friends
? They are not used by some higher level Feature, they are only used by Main
, so shouldn't they be inside it?
How do you organize those 2 cases, (1) Features managed by the TabBarController (that all sit beside each other), (2) Nested features like FriendDetails
that can be presented in a UINavigationController stack style?
Good structure but how to deal with multiple Storyboards ? Do each context need separate storyboard ? Then the number of storyboard increases and it will be similar to Xib files..! What do you recommend in this case ?
What about plists, core data models, bridging header, and entitlements?
I really liked this suggestion. But I want to suggest a structure for files like info.plist and network layer and others. And another approach to initial configuration files.
|-- Application
|-- Home
|-- Views
|-- Models
|-- Controllers/ViewModels
|-- UI
|-- Login
|-- Views
|-- Models
|-- Controllers/ViewModels
|-- UI
|-- Common
|-- Extensions
|-- Assets
|-- Images.xcassets
|-- Sounds
|-- SupportFiles
|-- Info.plist
|-- Entitlements
|-- Bridging Header
|-- Network
|-- Bootstrap
|-- AppDelegate.swift
|-- SceneDelegate.swift
|-- LaunchScreen.storyboard
In your guide, you have
I find this gets hairy as the app gets larger, especially if you're making the same nested folders for features in each of the top-tier folders (e.g. Views/Login/{LoginHeaderView.swift} and Controllers/Login/{LoginViewController.swift})
I really recommend
For example:
^ Shallow folder structure. Organized by context first.
Now compare that to (using the same files) your structure:
^ Deep folder structure. Also redundant. Organized by class type first.
Not really sure where Assets go in your model, but I imagine it's not much different than mine.
Working on larger teams, and then smaller teams that quickly become larger teams, the latter is much more manageable.
Some bonuses to the model I propose.
1) It's easier to comprehend everything in terms of context, instead of sorting by "kind" of file.
To me, it's the difference between organizing by project or by file type (in this case, model, view, controller). Imagine the scenario where you're giving a presentation and that presentation has a .doc, a .ppt, and a .pdf file, but instead of putting them in 1 folder called "Important Presentation to CEO" you put them in 3 separate folders (Documents, Powerpoint, PDF), each containing a subfolder called "Important Presentation to CEO" and then the folder. We'd never do this :P We organize by context, not kind, in every other project we build.. why not for our apps?
2) Many less merge conflicts in proj file. If teammate A is working on login and you're working on feed, it is very unlikely you will add files into each other's structure! However, in your model, we have to. Every feature means we probably added files to Models, Controllers, Views, etc. Merge conflict galore!
The model you propose reminds me of Ruby on Rails. Do you have a web frontend background?
Oh! And you can still easily search for your models, views, or viewControllers if you have a common naming pattern.
e.g. name all your models something like UserModel.swift and PostModel.swift viewControllers as LoginViewController managers as LoginManager non-view controllers as FeedController extensions as +Additions
And typing 'model' 'controller' 'viewcontroller' or '+Additions' in the bottom left corner of xcode should narrow your search similar to your model :)