Зручна структура iOS проекту

У мережі можна знайти безліч туторіалів, які навчають роботі з вбудованими фреймворками і сторонніми бібліотеками для iOS. Але рідко можна зустріти статті, які розповідають про спільні речі. Таких, як, наприклад, створення зручної структури проекту.

Швидше за все, досвідчені розробники давно користуються цими прийомами. Але я постійно стикаюся з проектами, які робили, не дотримуючись цих простих правил.

У маленьких проектах і різних POCax грамотна структура, можливо, не має великого сенсу. Але кожен маленький проект може стати великим і складним. Тому часто зустрічаються ситуації, коли розробник з досвідом тільки маленьких проектів починає робити великий за тими ж правилами.

Він створює файли вью контролерів в одну теку, в іншу кладе всі допоміжні класи. У підсумку проект розростається і у величезному дереві вже складно щось знайти, якщо не мати в голові карти. Новому розробнику доводиться запам'ятовувати, що де лежить, або реструктурувати весь проект.

Мета:

  • Організувати файли так щоб часто використовувані були під рукою, а рідко використовувані були в логічних категоріях щоб їх легко можна було знайти;
  • Створює однакову структуру файлів у всіх проектах.

Якщо у всіх проектах ви використовуєте одну структуру, відкривши старий проект навіть через рік можна швидко знайти потрібний клас.

Для мене головним критерієм оцінки структури є частота використання ⇧⌘O (Open Quickly) і ⌥⌘J (Filter in Navigation). Навіть у добре структурованих проектах я використовую ці поєднання, але тільки якщо мені потрібно швидко стрибнути до файлу або відкрити в дереві теку, що містить його. Це просто швидше, ніж відкривати стрілочки в дереві проекту.

Основні ідеї

Дерево проекту має повністю відповідати реальній структурі тек на диску.

По-перше, Xcode під час створення нового файлу пропонуватиме правильну теку для збереження та розміщення файлів у правильному місці в структурі:

По-друге, добре орієнтуючись у дереві проекту можна так само швидко знайти файл у теці на диску. Структура ідентична.

Якщо це правило не дотримуватися, Xcode буде пропонувати зберегти файли до корінної теки програми і може додавати їх до дерева перед проектом. Слід вручну вибирати теку для файла, потім переносити файли в дереві і видаляти ті, які додалися неправильно. Неприємно.

Йдемо по теках

Взагалі, я прихильник алфавітного порядку файлів. У мене навіть є спеціальний шорткат для сортування рядків в алфавітному порядку. Використовую # import, наприклад. Але з будь-якого правила є винятки. На верхньому рівні дерева порядок тек особливий. Це комбінація з сортування за частотою використання і суб'єктивної логічності.

Controllers, Models і Views зберігають в собі контролери, моделі і уявлення. Дотримуємося MVC.

Наприклад, у нас є view controller для відображення даних про подію. Тоді спадкоємець UIViewController ляже в Controllers/Event. Об'єкт Event отриманий, наприклад, з API ми покладемо в Models/Event. У Views/Event ми можемо покласти, наприклад, в'ю яке відображає аватарку автора події, його ім'я і карму. Об'єкт EventAuthorView у теці Event author:

Здавалося б, простіше іменувати папки так само як і класи: наприклад EventAuthorView клас і EventAuthorView тека. Тоді можна натиснути Enter на імені, ⌘C, ⌘V і не виправляти назву теки. Але особисто я текст без прогалин сприймаю важко, тому під кінець дня починаю тупити над назвами папок. До того ж, теки іменуються один раз, а працювати з ними ще дуже довго. Значить я витрачу трохи часу на іменуванні і зекономлю значно більше в майбутньому на пошуку файлу.

Ще можна називати теки для класів, наприклад, Event view controller. Але практика показала що коли я заходжу в папку Controller в пошук EventViewController мій мозок робить substring і відсікає ViewController від всіх папок в пошуках заповітного Event. Я вже знаю, що зайшов в контролери і тут тільки вони. Тут не може бути в'ю або моделі. Емпіричним шляхом було визначено: Event простіше для сприйняття.

Слід уникати одночасного назви EventViewController, EventsViewController і тек Event, Events. Опікунки породжують дурні помилки.

У Library лежать всі допоміжні класи

Тут три теки:

Перша це Base classes, у ній лежать базові класи які використовуються повсюдно.

Это Model, Navigation controller и View controller. Може бути щось ще, наприклад Collection controller. Від цих класів успадковуються абсолютно всі відповідні сутності.

Заведіть собі за правило. Завжди успадковувати View controller від базового.

Навіть якщо зараз у цьому немає сенсу, в майбутньому обов'язково з'являться загальні методи.

Це ж правило діє для Navigation controller, моделей і всіх інших сутностей.

Друга тека - це Helpers. Тут лежать всі інші кастомні сутності.

Зазвичай у всіх проектах у мене є API, де лежить обгортка для AFNetworking, Base objects categories - тут категорії для об'єктів з SDK, Constants - в ній всі константи, Message center в якій фасад для UIAlertView і Singletone - тут реалізація синглтона.

Ці файли спроектовані максимально гнучкими, щоб можна було підлаштовуватися під вимоги будь-якого проекту.

Наприклад, обгортка для API надає чорну скриньку для роботи з сервером. Ми створюємо виклик api так: [FavoriteEventsAPI apiWithObject:completion:] І у вікні completion отримуємо вже розпарсені об'єкти, або помилку.

Якщо потрібно буде переїхати з parse.com на свій сервер, з xml на json або з одного ip на інший ми зробимо це всередині чорної скриньки, а для клієнта не буде змін.

Message center використовується для всіх висновків повідомлень користувачеві. Він же визначає яке повідомлення для якого об'єкта вивести. Наприклад, користувачеві не потрібно знати що у на помилка 404. Йому потрібно сказати: «вибач, щось пішло не так...» Але помилку «логін зайнятий» потрібно виводити, причому різними мовами. І, якщо ми будемо переїжджати з UIAlertView на щось більш красиве, ми зробимо це в одному місці.

Vendors - це тека для класів третіх осіб, яких немає в CocoaPods. У Storyboards лежать файли .storyboard.

В Application ось так:

Warnings.xcconfig взято тут: github.com/boredzo/Warnings-xcconfig

У мене включені всі ворнінги і вони трактуються як помилки. Плюс статик аналайзер перевіряє збірку. Включаю так:

Багатьом може здаватися надмірним такий підхід, але це допомагає уникати багатьох дурних помилок. Про той же статик аналайзер всі знають, але потрібно не забувати його включати періодично. До того ж він може показати потенційний склад ще до першого запуску. А не після довгих годин налагодження.

Ну і моє правило: 0 ворнінгів, 0 помилок, 0 помилок статик аналайзера.

З Resources все просто:

Планую зробити шаблон для проекту з такою структурою, але поки руки не доходять.

Приклад шаблону можна взяти тут: github.com/reidmain/Xcode-6-Project-Templates

Такий підхід допомагає мені керувати проектами будь-якого розміру і не губитися в достатку класів.

Пропозиції та зауваження вітаються.