Почему Storyboard — не лучшая идея

от автора

Итак, вы начали новый проект в Xcode. Первое, что я предлагаю сделать, это удалить Main.storyboard. Почему? Потому что от него исходит много проблем.

Чем мне не угодил Storyboard

Не спорю, Storyboard — очень удобная вещь. Все контроллеры расположены в одном месте, причем, все они соединены переходами (segues). Можно сказать, что приложение будто находится у вас на ладонях. И это замечательно, ведь не всегда удается запомнить, к какому контроллеру мы перейдем, если нажмем на очередную кнопку или ячейку.
Но, это все? Есть ли еще какие-нибудь преимущества? На самом деле нет. Зато приходится мириться с многими неприятными вещами.

Неприятность первая

Все контроллеры расположены в одном месте

А так ли это хорошо? Возможно, если в приложении их не так много. Но что обычно происходит при увеличении их количества? А вот что:

Storygetti

Выглядит не очень, да и поддерживать такое вряд ли кому-то захочется.
(Ах да, еще оно тормозит)

Неприятность вторая

Да, держать все контроллеры в одном месте это ужасно. Может ли быть что-нибудь хуже? Может. Например, если над таким большим приложением работает не один человек. Если двое разработчика занимаются интерфейсом, который находится в одном Storyboard’е, в разных ветках, то как им потом объединить эти изменения? Ответ простой — никак. Скорее всего, кому-то придется слить себе чужие изменения и сделать свою работу заново.
И чем чаще будут происходить такие моменты, тем горячее вам будет сидеть на стуле.

Неприятность третья

А вот и самая большая проблема для меня на текущий момент: передача зависимостей. Приведу небольшой пример:
Если следовать MVVM, то у каждого контроллера должна быть ViewModel, причем породить ее должна родительская ViewModel. Вот как это может выглядеть при использовании Storyboard:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {     if segue.identifier == Segues.MoviePreview {         let controller = segue.destinationViewController as? MoviePreviewController         controller?.movieViewModel = viewModel.movieViewModel()     } } 

При этом, MoviePreviewController выглядит как-то так:

class MoviePreviewController: UIViewController {      var movieViewModel: MovieViewModel?          // ...          override func viewDidLoad() {         super.viewDidLoad()                  if let movieTitle = movieViewModel?.title {             doSomethingWithMovieTitle(movieTitle)         } else {             // Report absence of title         }     }  } 

Проблема в том, что movieViewModel у нас Optional, хотя мы знаем, что там обязательно должно быть значение. Это выливается в совсем ненужные проверки при попытке извлечь нужные нам данные.

Некоторые скажут, что это совсем не проблема, ведь мы можем насильно развернуть значение с помощью специального оператора ! или, еще лучше, объявить movieViewModel как MovieViewModel! , и они будут правы. Такая возможность есть, но она приносит с собой еще большую проблему: крэши в рантайме.

Например, если в методе prepareForSegue(_:sender:) вместо segue.identifier == Segues.MoviePreview написать segue.identifier == Segues.HahaYouHaveAProblem , то мы можем быть уверены, что в скором времени приложение упадет.

Что можно сделать

Первые две неприятности решаются без каких-либо особых усилий. Если приложение должно работать на iOS 9+, то можно без зазрения совести воспользоваться Storyboard Reference и разделить один большой Storyboard на множество (в разумных пределах) мелких.

Правда, для iOS 7–8+ решение не будет таким же бесшовным и придется дописывать руками что-то наподобии такого:

let storyboard = UIStoryboard(name: Storyboards.SomeStory, bundle: nil) let viewController = storyboard.instantiateInitialViewController()   if let viewController = viewController {     presentViewController(viewController, animated: true, completion: nil) } 

И опять же, это ведет к потенциальным крэшам при инициализации Storyboard’a с неправильным именем.
А вот чтобы решить третью проблему, придется, ни много ни мало, вернуться к старым добрым .xib’ам.
И, в таком случае, MoviePreviewController может выглядеть так:

class MoviePreviewController: UIViewController {      var movieViewModel: MovieViewModel?          // ...          override func viewDidLoad() {         super.viewDidLoad()                  if let movieTitle = movieViewModel?.title {             doSomethingWithMovieTitle(movieTitle)         } else {             // Report absence of title         }     }  } 

И его инициализация:

@IBAction func buttonTapped(sender: AnyObject) {     let controller = MoviePreviewController(movieViewModel: viewModel.movieViewModel())     presentViewController(controller, animated: true, completion: nil) } 

Теряем ли мы что-нибудь, отказываясь от Storyboard в пользу .xib’ов? Ничего, кроме вышеперечисленного.

Ссылки:
Storyboard from hell

ссылка на оригинал статьи http://habrahabr.ru/post/275241/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *