Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggestion to change project directory organization #28

Open
jpmcglone opened this issue Sep 5, 2015 · 10 comments
Open

Suggestion to change project directory organization #28

jpmcglone opened this issue Sep 5, 2015 · 10 comments

Comments

@jpmcglone
Copy link

In your guide, you have

├─ Models
├─ Views
├─ Controllers (or ViewModels, if your architecture is MVVM)
├─ Stores
├─ Helpers

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

├─ Models
├─ Application // Not common views or controls, etc.
  ├─ Main
  ├─ Context A
  ├─ Context B
├─ Common
  ├─ Views/Controls
  ├─ Extensions
  ├─ Context X
├─ Assets

For example:

├─ Models
  ├─ User.swift // user model
  ├─ Post.swift  // post model
├─ Application
  ├─ Main
    ├─ AppDelegate.swift
    ├─ MainViewController.swift // Maybe your app is wrapped in a main view controller, could be a tab bar controller subclass, or some custom ViewController that has neat functionality
  ├─ Login
    ├─ LoginManager.swift // business logic. A singleton that any controller in the app can interact with
    ├─ LoginViewController.swift // view controller
    ├─ LoginHeaderView.swift  // a view that only belongs to login
  ├─ Feed
    ├─ FeedViewController.swift
    ├─ FeedTableViewCell.swift 
  ├─ Common
    ├─ Controls
      ├─ BouncingButton.swift // UIControl subclass that is a button w/ bouncing physics. Just an example
  ├─ Extensions
    ├─ UIView+Additions.swift // useful additions to UIView
    ├─ NSURL+Additions.swift // useful additions to NSURL
├─ Assets
  ├─ Images.xcassets
  ├─ Sounds
    ├─ success.wav
  ├─ Videos
    ├─ intro-onboarding.mp4

^ Shallow folder structure. Organized by context first.

Now compare that to (using the same files) your structure:

├─ Models
  ├─ User.swift
  ├─ Post.swift
├─ Views
  ├─ Login
    ├─ LoginHeaderView.swift   
  ├─ Feed
    ├─ FeedTableViewCell.swift 
  ├─ Common
    ├─ Controls
      ├─ BouncingButton.swift
├─ Controllers (or ViewModels, if your architecture is MVVM)
  ├─ Login
    ├─ LoginViewController.swift
  ├─ Feed
    ├─ FeedViewController.swift
├─ Stores
├─ Helpers
  ├─ Managers
     ├─ LoginManager.swift
  ├─ Extensions
    ├─ UIView+Additions.swift 
    ├─ NSURL+Additions.swift 

^ 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?

  1. 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 :)

@mkauppila
Copy link
Contributor

I really enjoyed reading your comprehensive walkthrough of the suggested project directory organization. Nice job 👍

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. ⛅

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?

@jpmcglone
Copy link
Author

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.

@erikjalevik
Copy link
Contributor

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.)

@richeterre
Copy link
Contributor

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 👍

@jpmcglone
Copy link
Author

@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!

@ghost
Copy link

ghost commented Nov 13, 2015

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?

@rodrigoruiz
Copy link

@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?

@abhijithpp
Copy link

abhijithpp commented Sep 5, 2016

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 ?

@Skalwalker
Copy link

What about plists, core data models, bridging header, and entitlements?

@leoniralves
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants